/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ /* * DBStatement.java * * Created on March 3, 2000 * */ package com.sun.jdo.spi.persistence.support.sqlstore.sql.generator; import org.netbeans.modules.dbschema.ColumnElement; import com.sun.jdo.spi.persistence.support.sqlstore.LogHelperSQLStore; import com.sun.jdo.spi.persistence.support.sqlstore.database.DBVendorType; import com.sun.jdo.spi.persistence.support.sqlstore.model.LocalFieldDesc; import com.sun.jdo.spi.persistence.utility.logging.Logger; import java.io.ByteArrayInputStream; import java.io.StringReader; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.*; /** */ public class DBStatement extends Object { /** Name of the batch threshold property. */ public static final String BATCH_THRESHOLD_PROPERTY = "com.sun.jdo.spi.persistence.support.sqlstore.BATCH_THRESHOLD"; /** * Batch threshold. Set the value from the system property named by * this class followed by "BATCH_THRESHOLD". Default is 100. */ private static final int BATCH_THRESHOLD = Integer.getInteger(BATCH_THRESHOLD_PROPERTY, 100).intValue(); /** The wrapped PreparedStatement. */ private PreparedStatement preparedStmt; /** Current number of batched commands. */ private int batchCounter = 0; /** The SQL text. */ private String statementText; /** The logger */ private static Logger logger = LogHelperSQLStore.getLogger(); /** * This constructor is used for batched updates. * @param conn the connection * @param statementText the statement text * @param timeout the query timeout */ public DBStatement(Connection conn, String statementText, int timeout) throws SQLException { this.statementText = statementText; preparedStmt = conn.prepareStatement(statementText); // Set SELECT/INSERT/UPDATE/DELETE Statement timeout if(timeout != -1) { // Avoid calling setQueryTimeOut when user has specified it as -1 // This can be used as a mechanism to prevent calling setQueryTimeOut // for drivers that do not support this call // for example, look at bug 6561160 preparedStmt.setQueryTimeout(timeout); } } /** Returns the SQL text. */ public String getStatementText() { return statementText; } /** Returns the wrapped PreparedStatement. */ public PreparedStatement getPreparedStatement() { return preparedStmt; } /** * Checks whether the current number of batched commands exceeds the * batch threshold defined by {@link #BATCH_THRESHOLD}. */ public boolean exceedsBatchThreshold() { return batchCounter >= BATCH_THRESHOLD; } /** * Increases the batch counter and delegates the addBatch call to the * PreparedStatement wrapped by this DBStatement. */ public void addBatch() throws SQLException { batchCounter++; if (logger.isLoggable(Logger.FINER)) { logger.finer("sqlstore.sql.generator.dbstatement.addbatch", // NOI18N new Integer(batchCounter)); } preparedStmt.addBatch(); } /** * Delegates the executeBatch call to the PreparedStatement wrapped by * this DBStatement and resets the batch counter. */ public int[] executeBatch() throws SQLException { if (logger.isLoggable(Logger.FINER)) { logger.finer("sqlstore.sql.generator.dbstatement.executebatch", // NOI18N new Integer(batchCounter)); } batchCounter = 0; return preparedStmt.executeBatch(); } /** * Delegates the executeUpdate call to the PreparedStatement wrapped by * this DBStatement. */ public int executeUpdate() throws SQLException { return preparedStmt.executeUpdate(); } /** * Delegates the executeQuery call to the PreparedStatement wrapped by * this DBStatement. */ public ResultSet executeQuery() throws SQLException { return preparedStmt.executeQuery(); } /** * Delegates the close call to the PreparedStatement wrapped by * this DBStatement. */ public void close() throws SQLException { if (preparedStmt != null) { preparedStmt.close(); } } /** * Binds the specified value to the column corresponding with * the specified index reference. * @param index the index * @param val the value * @param columnElement the columnElement corresponding to the parameter * marker at specified index. This parameter will always contain correct * value when called for sql statements corresponding to insert and update * For select statements this parameter can be null if query compiler is not * able to detect java field for a parameter or value passed to the query. * Please see RetrieveDescImpl#addValueConstraint for more information * * @param vendorType the vendor type * @throws SQLException thrown by setter methods on java.sql.PreparedStatement * @see com.sun.jdo.spi.persistence.support.sqlstore.sql.RetrieveDescImpl#addValueConstraint */ public void bindInputColumn(int index, Object val, ColumnElement columnElement, DBVendorType vendorType) throws SQLException { int sqlType = getSqlType(columnElement); if (logger.isLoggable(Logger.FINER)) { Object[] items = {new Integer(index),val,new Integer(sqlType)}; logger.finer("sqlstore.sql.generator.dbstatement.bindinputcolumn", items); // NOI18N } if (val == null) { //setNull is called only for insert and update statement to set a column //to null value. We will always have valid sqlType in this case preparedStmt.setNull(index, sqlType); } else { if (val instanceof Number) { Number number = (Number) val; if (number instanceof Integer) { preparedStmt.setInt(index, number.intValue()); } else if (number instanceof Long) { preparedStmt.setLong(index, number.longValue()); } else if (number instanceof Short) { preparedStmt.setShort(index, number.shortValue()); } else if (number instanceof Byte) { preparedStmt.setByte(index, number.byteValue()); } else if (number instanceof Double) { preparedStmt.setDouble(index, number.doubleValue()); } else if (number instanceof Float) { preparedStmt.setFloat(index, number.floatValue()); } else if (number instanceof BigDecimal) { preparedStmt.setBigDecimal(index, (BigDecimal) number); } else if (number instanceof BigInteger) { preparedStmt.setBigDecimal(index, new BigDecimal((BigInteger) number)); } } else if (val instanceof String) { bindStringValue(index, (String)val, columnElement, vendorType); } else if (val instanceof Boolean) { preparedStmt.setBoolean(index, ((Boolean) val).booleanValue()); } else if (val instanceof java.util.Date) { if (val instanceof java.sql.Date) { preparedStmt.setDate(index, (java.sql.Date) val); } else if (val instanceof Time) { preparedStmt.setTime(index, (Time) val); } else if (val instanceof Timestamp) { preparedStmt.setTimestamp(index, (Timestamp) val); } else { Timestamp timestamp = new Timestamp(((java.util.Date) val).getTime()); preparedStmt.setTimestamp(index, timestamp); } } else if (val instanceof Character) { bindStringValue(index, val.toString(), columnElement, vendorType); } else if (val instanceof byte[]) { // // We use setBinaryStream() because of a limit on the maximum // array size that can be bound using the // PreparedStatement class setBytes() method on Oracle. // //preparedStmt.setBytes(index, (byte[]) val); byte[] ba = (byte[]) val; preparedStmt.setBinaryStream(index, new ByteArrayInputStream(ba), ba.length); } else if (val instanceof Blob) { preparedStmt.setBlob(index, (Blob) val); } else if (val instanceof Clob) { preparedStmt.setClob(index, (Clob) val); } else { preparedStmt.setObject(index, val); } } } /** * Binds the specified value to the column corresponding with * the specified index reference. * @param index the index * @param strVal the value * @param columnElement Descripion of the database column. * @param vendorType the vendor type * @throws SQLException thrown by setter methods on java.sql.PreparedStatement */ private void bindStringValue(int index, String strVal, ColumnElement columnElement, DBVendorType vendorType) throws SQLException { int sqlType = getSqlType(columnElement); if(LocalFieldDesc.isCharLobType(sqlType) ) { //Correct sqlType is passed for parameter markers which do not belong to where clause //So for insert and update statement we can safely detect binding to character LOB here. // //For parameter markers belonging to where clause, we do not always receive correct sqlType. //It is not allowed by any db to have a Character LOB type in where clause except //for null comparison. Let the db report an error if user puts a field mapped //to character LOB column in where clause. preparedStmt.setCharacterStream(index, new StringReader(strVal), strVal.length()); } else if(LocalFieldDesc.isFixedCharType(sqlType) ) { vendorType.getSpecialDBOperation().bindFixedCharColumn(preparedStmt, index, strVal, getLength(columnElement) ); } else { preparedStmt.setString(index, strVal); } } private static int getSqlType(ColumnElement columnElement) { return (columnElement != null) ? columnElement.getType() : Types.OTHER; } private static int getLength(ColumnElement columnElement) { int length = -1; if(columnElement != null) { Integer l = columnElement.getLength(); if(l != null) { length = l.intValue(); } } return length; } }