com.viaoa.ds.jdbc.query.ResultSetIterator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of oa Show documentation
Show all versions of oa Show documentation
Object Automation library
The newest version!
/*
This software and documentation is the confidential and proprietary
information of ViaOA, Inc. ("Confidential Information").
You shall not disclose such Confidential Information and shall use
it only in accordance with the terms of the license agreement you
entered into with ViaOA, Inc.
ViaOA, Inc. MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, OR NON-INFRINGEMENT. ViaOA, Inc. SHALL NOT BE LIABLE FOR ANY DAMAGES
SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
THIS SOFTWARE OR ITS DERIVATIVES.
Copyright (c) 2001-2013 ViaOA, Inc.
All rights reserved.
*/
package com.viaoa.ds.jdbc.query;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.PrintWriter;
import java.sql.*;
import com.viaoa.object.*;
import com.viaoa.transaction.OATransaction;
import com.viaoa.util.ClassModifier;
import com.viaoa.util.OAConverter;
import com.viaoa.util.OADate;
import com.viaoa.util.OADateTime;
import com.viaoa.util.OAThrottle;
import com.viaoa.util.OATime;
import com.viaoa.ds.OADataSourceIterator;
import com.viaoa.ds.jdbc.*;
import com.viaoa.ds.jdbc.db.*;
import com.viaoa.hub.Hub;
public class ResultSetIterator implements OADataSourceIterator {
private static Logger LOG = Logger.getLogger(ResultSetIterator.class.getName());
OADataSourceJDBC ds;
Class clazz;
String query;
Statement statement;
PreparedStatement preparedStatement;
OATransaction transaction;
ResultSet rs;
Column[] columns;
Object[] values;
ColumnInfo[] columnInfos;
volatile boolean bMore = false;
int lastPkeyColumn; // last column needed to be able to create an ObjectKey, to do a cache lookup
int max;
int cnter;
// used when first selecting only the primary key column
String query2;
Statement statement2;
ResultSet rs2;
int idColumnCount;
OAObjectInfo oi;
Object[] pkeyValues;
boolean bDatesIncludeTime;
Object objectTrue, objectFalse;
boolean bDirty;
boolean bIsSelecting;
boolean bInit;
DataAccessObject dataAccessObject;
DataAccessObject.ResultSetInfo resultSetInfo = new DataAccessObject.ResultSetInfo();
Object[] arguments; // when using preparedStatement
private boolean bUsePreparedStatement;
public static final OAThrottle throttle = new OAThrottle(2500);
public String getQuery() {
return query;
}
public String getQuery2() {
return query2;
}
class ColumnInfo {
Column column;
int pkeyPos=-1;
}
public ResultSetIterator(OADataSourceJDBC ds, Class clazz, DataAccessObject dataAccessObject, String query, String query2, int max) {
this(ds, clazz, null, query, query2, max, dataAccessObject);
}
// 20121013 to use with preparedStatement
public ResultSetIterator(OADataSourceJDBC ds, Class clazz, DataAccessObject dataAccessObject, String query, Object[] arguments) {
this.ds = ds;
this.clazz = clazz;
this.dataAccessObject = dataAccessObject;
this.query = query;
this.arguments = arguments;
bUsePreparedStatement = true;
}
public ResultSetIterator(OADataSourceJDBC ds, Class clazz, Column[] columns, String query, Object[] arguments, int max) {
this(ds, clazz, columns, query, null, max, null);
this.arguments = arguments;
bUsePreparedStatement = true;
}
public ResultSetIterator(OADataSourceJDBC ds, Class clazz, Column[] columns, String query, int max) {
this(ds, clazz, columns, query, null, max, null);
}
/**
* @param query2 used if the first query only returns pkIds.
* Query2 will need to use ? to position where the id values will be inserted.
* Query2 needs to be the correct SQL statement.
*/
public ResultSetIterator(OADataSourceJDBC ds, Class clazz, Column[] columns, String query, String query2, int max) {
this(ds, clazz, columns, query, query2, max, null);
}
private ResultSetIterator(OADataSourceJDBC ds, Class clazz, Column[] columns, String query, String query2, int max, DataAccessObject dataAccessObject) {
// LOG.fine("query="+query+", query2="+query2+", columns.length="+columns.length+", max="+max);
this.ds = ds;
this.clazz = clazz;
this.columns = columns;
this.query = query;
this.query2 = query2;
this.max = max;
this.dataAccessObject = dataAccessObject;
}
public void setDirty(boolean b) {
this.bDirty = b;
}
public boolean getDirty() {
return this.bDirty;
}
protected synchronized void init() {
if (bInit) return;
bInit = true;
long ts = System.currentTimeMillis();
_init();
long msDiff = System.currentTimeMillis() - ts;
if (throttle.check() || msDiff > 3000) {
String txt = throttle.getCheckCount()+") ResultSetIterator: ";
txt += msDiff+"ms";
if (msDiff > 5000) txt += " ALERT";
String s = query;
int pos = s.toUpperCase().indexOf(" FROM ");
if (pos > 0) s = s.substring(pos+1);
pos = s.toUpperCase().indexOf("PASSWORD");
if (pos > 0) s = s.substring(0, pos) + "****";
txt += " query="+s;
if (msDiff > 3000) OAPerformance.LOG.fine(txt);
LOG.fine(txt);
//if (OAObject.getDebugMode()) {
System.out.println(txt);
//}
}
}
private void _init() {
/*
if ( (qqq%(DisplayMod*4)==0)) {
Vector v = OADataSource.getInfo();
for (int i=0; i hsObjectWasLoaded;
public synchronized Object next() {
if (!bInit) init();
if (!bMore && hubReadAhead == null) return null;
if (hubReadAhead == null) {
hubReadAhead = new Hub();
hsObjectWasLoaded = new HashSet<>(25, .75f);
}
hubReadAhead.remove(0); // remove last one that was returned from next(). It needs to stay in hubReadAhead in case getSiblings is called
for (int i=hubReadAhead.size(); bMore && i<100; i++) {
_next();
}
OAObject obj = (OAObject) hubReadAhead.getAt(0);
if (hsObjectWasLoaded.remove(obj.getGuid())) {
Hub holdDetailHub = OAThreadLocalDelegate.getGetDetailHub();
String holdDetailPP = OAThreadLocalDelegate.getGetDetailPropertyPath();
try {
OAThreadLocalDelegate.setGetDetailHub(this.hubReadAhead, null);
obj.afterLoad();
}
finally {
OAThreadLocalDelegate.resetGetDetailHub(holdDetailHub, holdDetailPP);
}
}
if (!bMore && hubReadAhead.size() == 1) {
close();
}
return obj;
}
protected boolean _next() {
if (!bInit) init();
if (rs == null) return false;
if (max > 0 && cnter > max) {
_close();
return false;
}
boolean bDataSourceLoadingObject = true;
OAObject oaObject = null;
boolean bLoadedObject = false;
boolean bSetChangedAndNew = false;
try {
ResultSet resultSet = rs;
if (query2 != null) { // need to do a seperate select to get data for each row
if (statement2 == null && ds != null) {
statement2 = ds.getStatement(query);
}
for (;bMore;) {
String newQuery = query2;
int pos = 0;
for (int i=0; i= 0) {
newQuery = newQuery.substring(0,pos) + s + newQuery.substring(pos+1);
if (s == null) pos += 4;
else pos += s.length();
}
else throw new RuntimeException("parameter mismatch in query "+query2);
}
statement2.setMaxRows(0);
rs2 = statement2.executeQuery(newQuery);
if (rs2.next()) {
resultSet = rs2;
break;
}
bMore = rs.next(); // goto next
rs2.close();
if (!bMore) return false;
}
}
if (!bDirty) {
OAThreadLocalDelegate.setLoading(true);
}
else bDataSourceLoadingObject = false;
if (!bDirty && dataAccessObject != null) {
resultSetInfo.reset(resultSet);
oaObject = dataAccessObject.getObject(resultSetInfo);
bLoadedObject = !resultSetInfo.getFoundInCache();
bSetChangedAndNew = true;
if (bLoadedObject) {
OAObject objx = (OAObject) OAObjectCacheDelegate.add(oaObject, false, true);
if (objx != oaObject) {
oaObject = objx;
}
}
}
else {
for (int i=0; i < columnInfos.length; i++) {
if (columnInfos[i].column.clazz.equals(String.class)) {
values[i] = resultSet.getString(i+1);
}
else if (columnInfos[i].column.clazz.equals(byte[].class)) {
// 20100514
Blob blob = resultSet.getBlob(i+1);
if (blob != null) {
values[i] = blob.getBytes(1, (int) blob.length());
}
else values[i] = null;
}
else {
values[i] = resultSet.getObject(i+1);
if (values[i] == null) {
}
else if (resultSet.wasNull()) {
values[i] = null;
}
else {
values[i] = convert(columnInfos[i].column.clazz, values[i]);
}
}
if (columnInfos[i].pkeyPos >= 0) {
pkeyValues[columnInfos[i].pkeyPos] = values[i];
if (i == lastPkeyColumn) {
// try to find existing object
oaObject = OAObjectCacheDelegate.get(clazz, new OAObjectKey(pkeyValues));
if (oaObject != null && !bDirty) break;
}
}
}
if (oaObject == null || bDirty) {
boolean bNew;
if (oaObject == null) {
bNew = true;
oaObject = (OAObject) OAObjectReflectDelegate.createNewObject(clazz);
}
else bNew = false;
for (int i=0; i < columns.length; i++) {
if (!bNew && columnInfos[i].pkeyPos >= 0) continue;
if (columnInfos[i].pkeyPos >= 0 || columns[i].fkeyLink == null) {
try {
oaObject.setProperty(columns[i].propertyName, values[i]);
}
catch (Exception e) {
if (bNew && columnInfos[i].pkeyPos >= 0) {
OAObject objx = OAObjectCacheDelegate.get(clazz, new OAObjectKey(pkeyValues));
if (objx != null) {
LOG.log(Level.WARNING, "Error while setting property "+columns[i].propertyName+", object has been found in cache, so everything is good", e);
oaObject = objx;
bNew = false;
if (!bDirty) break;
}
else {
LOG.log(Level.WARNING, "Error while setting property "+columns[i].propertyName+", NOT found in cache as hoped :( will continue anyway", e);
}
}
else {
LOG.log(Level.WARNING, "Error while setting property "+columns[i].propertyName+", will continue anyway", e);
}
}
}
else {
// fkey
if (columns[i].fkeyLink.fkeys.length == 1) {
oaObject.setProperty(columns[i].fkeyLink.propertyName, values[i]);
continue;
}
if (columns[i].fkeyLinkPos > 0) continue; // already loaded (in next code)
Object[] ids = new Object[columns[i].fkeyLink.fkeys.length];
for (int j=i; j < columns.length; j++) {
if (columns[j].fkeyLink == columns[i].fkeyLink) {
ids[columns[j].fkeyLinkPos] = values[j];
}
}
oaObject.setProperty(columns[i].fkeyLink.propertyName, new OAObjectKey(ids));
}
}
if (bNew && oi.getAddToCache()) { // 20110731 add to cache, OAThreadLocal.SkipObjectInitialize
oaObject = (OAObject) OAObjectCacheDelegate.add(oaObject, false, true);
}
OAObjectDelegate.setNew(oaObject, false);
oaObject.setChanged(false);
bLoadedObject = true;
bSetChangedAndNew = true;
}
}
++cnter;
if (bDataSourceLoadingObject) {
OAThreadLocalDelegate.setLoading(false);
bDataSourceLoadingObject = false;
}
if (bLoadedObject) {
hsObjectWasLoaded.add(oaObject.getGuid());
}
if (rs != null) {
bMore = rs.next(); // goto next
if (!bMore) _close();
}
hubReadAhead.add(oaObject);
return true;
}
catch (Exception e) {
String s = String.format("Exception in next(), thread=%s, query=%s, bClosed=%b", Thread.currentThread().getName(), query, bClosed);
LOG.log(Level.WARNING, s, e);
throw new RuntimeException(e);
}
finally {
if (bLoadedObject && !bSetChangedAndNew && oaObject != null) {
OAObjectDelegate.setNew(oaObject, false);
oaObject.setChanged(false);
}
if (bDataSourceLoadingObject) {
OAThreadLocalDelegate.setLoading(false);
}
}
}
private boolean bClosed;
// part of iterator interface
public void remove() {
_close();
}
public void finalize() throws Throwable {
super.finalize();
close();
}
// 20110407 added synchronized, since OASelectManager could close iterator while it is performing next()
public synchronized void close() {
if (hubReadAhead != null) {
hubReadAhead.clear();
hubReadAhead = null;
}
bClosed = true;
bMore = false;
_close();
}
protected void _close() {
try {
if (rs != null) {
rs.close();
rs = null;
}
if (rs2 != null) {
rs2.close();
rs2 = null;
}
if (transaction != null) {
transaction.commit();
}
if (statement != null) {
if (bIsSelecting) {
try {
statement.cancel();
}
catch (Exception exx) {
}
}
}
}
catch (Exception e) {
// throw new OADataSourceException(OADataSourceJDBC.this, "OADataSource.getStatement() "+e);
}
finally {
rs = null;
rs2 = null;
bMore = false;
if (ds != null) {
if (preparedStatement != null) { // 20121013
ds.getConnectionPool().releasePreparedStatement(preparedStatement, true);
}
else {
ds.releaseStatement(statement);
if (statement2 != null) ds.releaseStatement(statement2);
}
}
statement = null;
statement2 = null;
transaction = null;
preparedStatement = null;
}
}
private Object convert(Class paramType, Object obj) throws Exception {
if (obj == null) return null;
if (obj.getClass().equals(paramType)) return obj;
if (obj instanceof Clob) {
obj = ((Clob)obj).getSubString(1, (int) ((Clob)obj).length());
}
else if (obj.getClass().isArray()) {
// 2006/06/01
Class c = ClassModifier.getClassWrapper(paramType);
if (Number.class.isAssignableFrom(c) ) {
obj = new java.math.BigInteger((byte[]) obj);
}
else if (java.util.Date.class.isAssignableFrom(paramType) ) {
obj = new java.util.Date(new java.math.BigInteger((byte[]) obj).longValue());
}
else if (paramType.equals(String.class) ) { // 2006/11/08
obj = new String((byte[]) obj);
}
}
if (obj instanceof String) {
String s = (String) obj;
String fmt = null;
if (paramType.equals(String.class)) {
obj = repairSingleQuotes((String) obj);
}
else if (paramType.equals(int.class)) obj = Integer.valueOf(s);
else if (paramType.equals(double.class)) obj = Double.valueOf(s);
else if (paramType.equals(long.class)) obj = Long.valueOf(s);
else if (paramType.equals(short.class)) obj = Short.valueOf(s);
else if (paramType.equals(float.class)) obj = Float.valueOf(s);
else if (paramType.equals(char.class)) obj = new Character(s.charAt(0));
else {
if ( java.util.Date.class.isAssignableFrom(paramType) ) {
if (bDatesIncludeTime) fmt = "yyyy-MM-dd hh:mm:ss.SSS"; // 1999-11-21 14:21:53.123
else {
fmt = "yyyy-MM-dd"; // 1999-11-21
if (paramType.equals(Time.class)) fmt = "hh:mm:ss.SSS"; // 14:21:53
}
}
else {
if (bDatesIncludeTime) fmt = "yyyy-MM-dd hh:mm:ss.SSS"; // 1999-11-21 14:21:53.123
else {
if (paramType.equals(OADate.class)) fmt = "yyyy-MM-dd";
else if (paramType.equals(OATime.class)) fmt = "hh:mm:ss.SSS";
else if (paramType.equals(OADateTime.class)) fmt = "yyyy-MM-dd hh:mm:ss.SSS";
}
}
obj = OAConverter.convert(paramType, (String) obj, fmt);
}
}
else if (obj instanceof Number) {
Number num = (Number) obj;
if (paramType.equals(int.class)) obj = new Integer(num.intValue());
else if (paramType.equals(boolean.class)) obj = new Boolean(num.intValue() != 0);
else if (paramType.equals(double.class)) obj = new Double(num.doubleValue());
else if (paramType.equals(String.class)) obj = num.toString();
else if (paramType.equals(long.class)) obj = new Long(num.longValue());
else if (paramType.equals(short.class)) obj = new Short(num.shortValue());
else if (paramType.equals(float.class)) obj = new Float(num.floatValue());
else if (paramType.equals(char.class)) obj = new Character((char) num.shortValue());
else if (paramType.equals(java.awt.Color.class)) obj = new java.awt.Color(num.intValue());
}
else if (obj instanceof Double && paramType.equals(float.class)) {
obj = new Float( ((Double)obj).floatValue() ) ;
}
else if (obj instanceof java.util.Date) {
if (paramType.equals(Time.class)) obj = new Time( ((java.util.Date)obj).getTime() );
else if (paramType.equals(java.sql.Timestamp.class)) obj = new Timestamp( ((java.util.Date)obj).getTime() );
else if (paramType.equals(OADate.class)) obj = new OADate((java.util.Date)obj);
else if (paramType.equals(OATime.class)) obj = new OATime( (java.util.Date)obj );
else if (paramType.equals(OADateTime.class)) obj = new OADateTime( (java.util.Date)obj ); // 2006/11/08
}
else if (obj instanceof Boolean) {
boolean b = ((Boolean) obj).booleanValue();
if (paramType.equals(boolean.class));
else if (paramType.equals(int.class)) obj = new Integer(b?1:0);
else if (paramType.equals(double.class)) obj = new Double(b?1.0:0.0);
else if (paramType.equals(String.class)) obj = obj.toString();
else if (paramType.equals(long.class)) obj = new Long((long) (b?1:0));
else if (paramType.equals(short.class)) obj = new Short((short)(b?1:0));
else if (paramType.equals(float.class)) obj = new Float((float)(b?1.0f:0.0f));
else if (paramType.equals(char.class)) obj = new Character((char) (b?'1':'0'));
}
if (paramType.equals(boolean.class)) {
if (!(obj instanceof Boolean)) {
if (objectTrue == null || objectFalse == null) {
if (obj instanceof Number) new Boolean( ((Number)obj).intValue()!=0);
//else throw new OADataSourceException(OADataSourceJDBC.this,"ResultSetIterator.next() "+" method "+method.getName()+" uses a boolean and database stores data as "+obj.getClass());
}
else {
if (obj.equals(objectTrue)) {
obj = new Boolean(true);
}
else if (obj.equals(objectFalse)) {
obj = new Boolean(false);
}
else {
// throw new OADataSourceException(OADataSourceJDBC.this,"ResultSetIterator.next() "+" method "+method.getName()+" cant convert "+obj+" to a boolean, it does not match objectTrue or objectFalse values");
}
}
}
}
return obj;
}
protected String repairSingleQuotes(String value) {
return value;
}
}