/*******************************************************************************
* Copyright (c) 2014 BestSolution.at and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* tom <FIRSTNAME.LASTNAME@bestsolution.at> - initial API and implementation
*******************************************************************************/
package at.bestsolution.persistence.java.spi;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import org.apache.log4j.Logger;
import at.bestsolution.persistence.DynamicSelectQuery;
import at.bestsolution.persistence.MappedQuery;
import at.bestsolution.persistence.MappedUpdateQuery;
import at.bestsolution.persistence.expr.Expression;
import at.bestsolution.persistence.expr.PropertyExpression;
import at.bestsolution.persistence.java.DatabaseSupport;
import at.bestsolution.persistence.java.JDBCConnectionProvider;
import at.bestsolution.persistence.java.JavaObjectMapper;
import at.bestsolution.persistence.java.internal.PreparedExtendsInsertStatement;
import at.bestsolution.persistence.java.internal.PreparedInsertStatement;
import at.bestsolution.persistence.java.internal.PreparedStatement.Column;
import at.bestsolution.persistence.java.internal.PreparedUpdateStatement;
import at.bestsolution.persistence.java.query.DynamicListDelegate;
import at.bestsolution.persistence.java.query.DynamicSelectQueryImpl;
import at.bestsolution.persistence.java.query.ListDelegate;
import at.bestsolution.persistence.java.query.MappedQueryImpl;
import at.bestsolution.persistence.java.query.MappedUpdateQueryImpl;
import at.bestsolution.persistence.java.query.UpdateDelegate;
public class OracleDatabaseSupport implements DatabaseSupport {
static final Logger LOGGER = Logger.getLogger(OracleDatabaseSupport.class);
private JDBCConnectionProvider connectionProvider;
public void registerJDBCConnectionProvider(JDBCConnectionProvider connectionProvider) {
this.connectionProvider = connectionProvider;
}
@Override
public String getDatabaseType() {
return "Oracle";
}
@Override
public QueryBuilder createQueryBuilder(JavaObjectMapper<?> rootMapper, String tableName) {
return new OracleQueryBuilder(this,tableName, rootMapper, connectionProvider);
}
@Override
public PrimaryKeyGenType getPrimaryKeyType() {
return PrimaryKeyGenType.SEQUENCE;
}
@Override
public <O> MappedQuery<O> createMappedQuery(JavaObjectMapper<?> rootMapper,
String rootPrefix, ListDelegate<O> listDelegate) {
return new OracleMappedQuery<O>(this,rootMapper, rootPrefix, listDelegate);
}
@Override
public <O> MappedUpdateQuery<O> createMappedUpdateQuery(
JavaObjectMapper<O> rootMapper, String rootPrefix,
UpdateDelegate<O> updateDelegate) {
return new OracleMappedUpdateQuery<O>(this,rootMapper, rootPrefix, updateDelegate);
}
@Override
public <T, O> DynamicSelectQuery<T, O> createMappedSelectQuery(
JavaObjectMapper<?> rootMapper, String rootPrefix,
DynamicListDelegate<T, O> listDelegate) {
return new OracleSelectQuery<T,O>(this,rootMapper, rootPrefix, listDelegate);
}
@Override
public boolean isArrayStoreSupported(Class<?> type) {
return false;
}
@Override
public boolean isNestedResultSetsSupported() {
return false;
}
@Override
public Timestamp getServerTime(Connection connection) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("NOT IMPLEMENTED");
// return null;
}
@Override
public boolean isDefaultLowerCase() {
return false;
}
static class OracleMappedUpdateQuery<O> extends MappedUpdateQueryImpl<O> {
public OracleMappedUpdateQuery(DatabaseSupport db,JavaObjectMapper<O> rootMapper, String rootPrefix, UpdateDelegate<O> updateDelegate) {
super(db,rootMapper, rootPrefix, updateDelegate);
}
@Override
protected void appendCriteria(StringBuilder b, JavaObjectMapper<?> mapper, String colPrefix, Expression<O> expression) {
switch (expression.type) {
case ILIKE:
b.append("lower(" +colPrefix + quoteColumnName(mapper.getColumnName(((PropertyExpression<O>)expression).property)) + ") LIKE lower ( ? )" );
return;
case NOT_ILIKE:
b.append("lower(" +colPrefix + quoteColumnName(mapper.getColumnName(((PropertyExpression<O>)expression).property)) + ") NOT LIKE lower ( ? )" );
return;
default:
super.appendCriteria(b, mapper, colPrefix, expression);
}
}
@Override
public String processSQL(String sql) {
return sql;
}
}
static class OracleMappedQuery<O> extends MappedQueryImpl<O> {
public OracleMappedQuery(DatabaseSupport db,JavaObjectMapper<?> rootMapper, String rootPrefix, ListDelegate<O> listDelegate) {
super(db,rootMapper, rootPrefix, listDelegate);
}
@Override
protected void appendCriteria(StringBuilder b, JavaObjectMapper<?> mapper, String colPrefix,
Expression<O> expression) {
switch (expression.type) {
case ILIKE:
b.append("lower(" +colPrefix + mapper.getColumnName(((PropertyExpression<O>)expression).property) + ") LIKE lower ( ? )" );
return;
case NOT_ILIKE:
b.append("lower(" +colPrefix + mapper.getColumnName(((PropertyExpression<O>)expression).property) + ") NOT LIKE lower ( ? )" );
return;
default:
super.appendCriteria(b, mapper, colPrefix, expression);
}
}
@Override
public String processSQL(String sql) {
if( getMaxRows() != -1) {
//FIXME this needs to be cleverer
if( sql.contains("WHERE") ) {
sql = sql + " AND ";
} else {
sql = sql + " WHERE ";
}
sql = sql + "ROWNUM <= " + getMaxRows();
}
return sql;
}
}
static class OracleSelectQuery<T,O> extends DynamicSelectQueryImpl<T,O> {
public OracleSelectQuery(DatabaseSupport db,JavaObjectMapper<?> rootMapper, String rootPrefix, DynamicListDelegate<T,O> listDelegate) {
super(db,rootMapper, rootPrefix, listDelegate);
}
@Override
protected void appendCriteria(StringBuilder b, JavaObjectMapper<?> mapper, String colPrefix,
Expression<O> expression) {
switch (expression.type) {
case ILIKE:
b.append("lower(" +colPrefix + mapper.getColumnName(((PropertyExpression<O>)expression).property) + ") LIKE lower ( ? )" );
return;
case NOT_ILIKE:
b.append("lower(" +colPrefix + mapper.getColumnName(((PropertyExpression<O>)expression).property) + ") NOT LIKE lower ( ? )" );
return;
default:
super.appendCriteria(b, mapper, colPrefix, expression);
}
}
@Override
public String processSQL(String sql) {
if( getMaxRows() != -1) {
//FIXME this needs to be cleverer
if( sql.contains("WHERE") ) {
sql = sql + " AND ";
} else {
sql = sql + " WHERE ";
}
sql = sql + "ROWNUM <= " + getMaxRows();
}
return sql;
}
}
static class OracleQueryBuilder implements QueryBuilder {
private final String tableName;
private final JDBCConnectionProvider connectionProvider;
private final JavaObjectMapper<?> rootMapper;
private final DatabaseSupport db;
public OracleQueryBuilder(DatabaseSupport db, String tableName, JavaObjectMapper<?> rootMapper, JDBCConnectionProvider connectionProvider) {
this.db = db;
this.tableName = tableName;
this.connectionProvider = connectionProvider;
this.rootMapper = rootMapper;
}
@Override
public UpdateStatement createUpdateStatement(String pkColumn, String lockColumn) {
return new PreparedUpdateStatement(db, tableName, pkColumn, lockColumn);
}
@Override
public ExtendsInsertStatement createExtendsInsertStatement(String pkColumn) {
return new PreparedExtendsInsertStatement(db,tableName, pkColumn);
}
@Override
public InsertStatement createInsertStatement(String pkColumn, String primaryKeyExpression, String lockColumn) {
return new OracleInsertStatement(db,tableName, pkColumn, primaryKeyExpression, lockColumn, rootMapper, connectionProvider);
}
}
static class OracleInsertStatement extends PreparedInsertStatement {
private final JDBCConnectionProvider connectionProvider;
private final JavaObjectMapper<?> rootMapper;
public OracleInsertStatement(DatabaseSupport db, String tableName, String pkColumn,
String primaryKeyExpression, String lockColumn, JavaObjectMapper<?> rootMapper, JDBCConnectionProvider connectionProvider) {
super(db,tableName, pkColumn, primaryKeyExpression, lockColumn);
this.rootMapper = rootMapper;
this.connectionProvider = connectionProvider;
}
@Override
public void addBlob(String column, Blob value) {
columnList.add(new OracleBlobColumn(rootMapper, columnList.size(), column, value, connectionProvider));
}
@Override
protected long execute(PreparedStatement pstmt) throws SQLException {
try {
return super.execute(pstmt);
} finally {
boolean isDebug = LOGGER.isDebugEnabled();
for( Column c : columnList ) {
if( c instanceof OracleBlobColumn ) {
if( isDebug ) {
LOGGER.debug("Freeing oracle blob for column '"+c+"'");
}
((OracleBlobColumn) c).release(pstmt.getConnection());
}
}
}
}
}
static class OracleBlobColumn extends Column {
private final Blob blob;
private final JDBCConnectionProvider connectionProvider;
private Blob tempBlob;
private final JavaObjectMapper<?> rootMapper;
public OracleBlobColumn(JavaObjectMapper<?> rootMapper, int index, String column, Blob blob, JDBCConnectionProvider connectionProvider) {
super(index, column);
this.rootMapper = rootMapper;
this.blob = blob;
this.connectionProvider = connectionProvider;
}
@Override
public void apply(java.sql.PreparedStatement pstmt) throws SQLException {
if (LOGGER.isDebugEnabled()) LOGGER.debug("Parameter " + (index+1) + " => Blob(" + blob.length() + ")");
tempBlob = connectionProvider.createTempBlob(rootMapper.getSession().getConfigurationId(), pstmt.getConnection());
OutputStream oracleStream = tempBlob.setBinaryStream(0);
InputStream inputStream = blob.getBinaryStream();
try {
byte[] buf = new byte[1024];
int l = 0;
while( (l = inputStream.read(buf)) != -1 ) {
oracleStream.write(buf, 0, l);
}
inputStream.close();
pstmt.setBlob(index+1, tempBlob);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void release(Connection connection) throws SQLException {
if( tempBlob != null ) {
connectionProvider.releaseTempBlob(rootMapper.getSession().getConfigurationId(),connection, blob);
}
}
}
}