gu.sql2java.BaseTableManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sql2java-manager Show documentation
Show all versions of sql2java-manager Show documentation
sql2java manager class package for accessing database
package gu.sql2java;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import gu.sql2java.ForeignKeyMetaData.ForeignKeyRule;
import gu.sql2java.Manager.AutoKeyRetrieveType;
import gu.sql2java.exception.DaoException;
import gu.sql2java.exception.DataAccessException;
import gu.sql2java.exception.DataRetrievalException;
import gu.sql2java.exception.ObjectRetrievalException;
import gu.sql2java.exception.OptimisticLockingException;
import gu.sql2java.exception.RuntimeDaoException;
import static com.google.common.base.Preconditions.*;
import static gu.sql2java.SimpleLog.*;
import static gu.sql2java.Managers.*;
/**
* implementation of {@link TableManager}
* @author guyadong
*
* @param java bean type
*/
class BaseTableManager implements TableManager,Constant{
protected final RowMetaData metaData;
/** lazy load */
private volatile Map> foreignKeyDeleteListeners;
/** lazy load */
private volatile ListenerContainer listenerContainer;
/** lazy load */
private volatile String generatedkeyStatement;
private static boolean debug = false;
protected BaseTableManager(String tablename){
metaData = RowMetaData.getMetaData(tablename);
}
protected ListenerContainer getListenerContainer() {
// double checking
if(listenerContainer == null){
synchronized (this) {
if(listenerContainer == null){
listenerContainer = new ListenerContainer();
}
}
}
return listenerContainer;
}
/**
* @return map with foreignKey name TO TableListener
*/
protected Map> getForeignKeyDeleteListeners(){
// double checking
if(foreignKeyDeleteListeners == null){
synchronized (this) {
if(foreignKeyDeleteListeners == null){
LinkedHashMap> map = Maps.newLinkedHashMap();
for(ForeignKeyMetaData fk : metaData.getForeignKeysForListener()){
map.put(fk.name, new DeleteRuleListener(fk.name));
}
foreignKeyDeleteListeners = Collections.unmodifiableMap(map);
}
}
}
return foreignKeyDeleteListeners;
}
private String getGeneratedkeyStatement(Connection c){
// double check
if(generatedkeyStatement == null){
synchronized (this) {
if(generatedkeyStatement == null){
generatedkeyStatement = checkNotNull(getManager().getGeneratedkeyStatement(c),"INVALID generatedkeyStatement")
.replaceAll("", metaData.tablename )
.replaceAll("", metaData.columnNameOf(metaData.autoincrementColumnId));
if(debug){
log("generatedkeyStatement={}",generatedkeyStatement);
}
}
}
}
return generatedkeyStatement;
}
/**
* Creates a new B instance.
*
* @return the new B instance
*/
@SuppressWarnings("unchecked")
protected final B createBean()
{
try {
return (B) metaData.beanType.newInstance();
} catch (Exception e) {
Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
}
}
/**
* Creates a new B instance.
* @param primaryValues values of primary keys
* @return B instance
*/
protected final B createBean(Object... primaryValues)
{
checkArgument(primaryValues != null && primaryValues.length== metaData.primaryKeyNames.length,"INVALID primaryValues");
B bean = createBean();
int[] pkIds = metaData.primaryKeyIds;
for(int i=0;iboolean hasNullPk(T bean){
return (null == bean || hasNull(bean.primaryValues()));
}
private void prepareAutoincrement( Connection c,PreparedStatement ps, B bean) throws SQLException
{
if (!bean.isModified(metaData.autoincrementColumnId))
{
PreparedStatement ps2 = null;
ResultSet rs = null;
try {
AutoKeyRetrieveType retrieveType = getManager().getGeneratedkeyRetrieveType(c);
if(AutoKeyRetrieveType.auto.equals(retrieveType)){
rs = ps.getGeneratedKeys();
}else{
ps2 = c.prepareStatement(getGeneratedkeyStatement(c));
rs = ps2.executeQuery();
}
if(rs.next()) {
setColumnValue(bean,
metaData.autoincrementColumnId,
Manager.getObject(rs,1,metaData.columnTypes.get(metaData.autoincrementColumnId)));
} else {
throw new IllegalStateException(logString("ATTENTION: Could not retrieve generated key!(retrieveType:{})",retrieveType));
}
} finally {
getManager().close(ps2, rs);
}
}
}
//13
/**
* Insert the B bean into the database.
*
* @param bean the B bean to be saved
* @return the inserted bean
* @throws RuntimeDaoException
*/
protected B insert(final B bean)
{
// mini checks
if (null == bean || !bean.isModified()) {
return bean;
}
if (!bean.isNew()){
return this.update(bean);
}
Connection c = null;
PreparedStatement ps = null;
try
{
c = this.getConnection();
final AutoKeyRetrieveType retrieveType = getManager().getGeneratedkeyRetrieveType(c);
//-------------writePreInsert
if(metaData.autoincrementColumnId >= 0
&& AutoKeyRetrieveType.before.equals(retrieveType)){
prepareAutoincrement(c, null, bean);
}
//------------/writePreInsert
// listener callback
getListenerContainer().beforeInsert(bean);
StringBuilder sql = new StringBuilder("INSERT into " + metaData.tablename + " (");
List modifiedList = Lists.newArrayList(Iterables.filter(metaData.columnNames,new Predicate() {
@Override
public boolean apply(String input) {
return bean.isModified(input);
}
}));
String fields=Joiner.on(",").join(modifiedList);
// fields
sql.append(fields);
sql.append(") values (");
// values
sql.append(fields.replaceAll("[^,]+", "?"));
sql.append(")");
if(debug){
log("insert : " + sql.toString());
}
if(metaData.autoincrementColumnId >= 0 && AutoKeyRetrieveType.auto.equals(retrieveType)){
ps = c.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
}else{
ps = c.prepareStatement(sql.toString(), ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}
fillPreparedStatement(ps, bean, SEARCH_EXACT, true);
ps.executeUpdate();
//------------------writePostInsert
if(metaData.autoincrementColumnId >= 0 && !AutoKeyRetrieveType.before.equals(retrieveType)){
prepareAutoincrement(c, ps, bean);
}
//-------------------/writePostInsert
bean.setNew(false);
bean.resetIsModified();
// listener callback
getListenerContainer().afterInsert(bean);
return bean;
}
catch(SQLException e)
{
throw new RuntimeDaoException(new DataAccessException(e));
}
finally
{
// listener callback
getListenerContainer().done();
getManager().close(ps);
freeConnection(c);
}
}
private static final Function SQL_FUN1 = new Function(){
@Override
public String apply(String input) {
return input + "=?";
}};
//14
/**
* Update the B bean record in the database according to the changes.
*
* @param bean the B bean to be updated
* @return the updated bean
* @throws RuntimeDaoException
*/
protected B update(final B bean) throws RuntimeDaoException
{
// mini checks
if (null == bean || !bean.isModified()) {
return bean;
}
if (bean.isNew()){
return this.insert(bean);
}
Connection c = null;
PreparedStatement ps = null;
try
{
c = this.getConnection();
// listener callback
getListenerContainer().beforeUpdate(bean);
Object oldLockValue = null;
if(metaData.lockColumnType != null){
oldLockValue = bean.getValue(metaData.lockColumnName);
// lockColumnType is String or Long
if(String.class == metaData.lockColumnType){
bean.setValue(metaData.lockColumnName,String.valueOf(System.currentTimeMillis()));
}else if(Long.class ==metaData.lockColumnType){
bean.setValue(metaData.lockColumnName,System.currentTimeMillis());
}else{
throw new RuntimeException(logString("INVALID LOCK COLUMN TYPE:{},String or Long required",metaData.lockColumnType));
}
}
StringBuilder sql = new StringBuilder("UPDATE " + metaData.tablename + " SET ");
List modified = Lists.newArrayList(Iterables.filter(metaData.columnNames,new Predicate() {
@Override
public boolean apply(String input) {
return bean.isModified(input);
}
}));
sql.append(Joiner.on(",").join(Lists.transform(modified,SQL_FUN1)));
sql.append(" WHERE ");
sql.append(Joiner.on(" AND ").join(
Lists.transform(Arrays.asList(metaData.primaryKeyNames), SQL_FUN1)));
if(metaData.lockColumnType != null){
if(metaData.primaryKeyNames.length > 0){
sql.append(" AND ");
}
checkArgument(metaData.lockColumnName != null, "NOT DEFINED lock column name");
sql.append(metaData.lockColumnName + "=?");
}
if(debug){
log("update : " + sql.toString());
}
ps = c.prepareStatement(sql.toString(),
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
int dirtyCount = this.fillPreparedStatement(ps, bean, SEARCH_EXACT,true);
if (dirtyCount == 0) {
if(debug){
log("The bean to look is not initialized... do not update.");
}
return bean;
}
int[] pkIds = metaData.primaryKeyIds;
for(int i = 0; i {
final List list;
public ListAction() {
list=new LinkedList<>();
}
public List getList() {
return list;
}
@Override
public void call(B bean) {
list.add(bean);
}
}
@Override
public int countAll()throws RuntimeDaoException{
return this.countWhere("");
}
@Override
public int countUsingTemplate(B bean)throws RuntimeDaoException{
return this.countUsingTemplate(bean, SEARCH_EXACT);
}
@Override
public int deleteAll()throws RuntimeDaoException{
return this.deleteByWhere("");
}
@Override
public B[] loadAll()throws RuntimeDaoException{
return this.loadByWhere(null);
}
@Override
public int loadAll(TableManager.Action action)throws RuntimeDaoException{
return this.loadByWhere(null, action);
}
@SuppressWarnings("unchecked")
@Override
public B[] loadAll(int startRow, int numRows)throws RuntimeDaoException{
return this.loadByWhereAsList(null, null, startRow, numRows).toArray((B[]) Array.newInstance(metaData.beanType, 0));
}
@Override
public int loadAll(int startRow, int numRows, TableManager.Action action)throws RuntimeDaoException{
return this.loadByWhereForAction(null, null, startRow, numRows, action);
}
@Override
public List loadAllAsList()throws RuntimeDaoException{
return this.loadUsingTemplateAsList(null,1, -1, SEARCH_EXACT);
}
@Override
public List loadAllAsList(int startRow, int numRows)throws RuntimeDaoException{
return this.loadUsingTemplateAsList(null, startRow, numRows, SEARCH_EXACT);
}
@Override
public B loadByPrimaryKey(B bean)throws RuntimeDaoException{
return bean==null ? null : loadByPrimaryKey(bean.primaryValues());
}
@Override
public B loadByPrimaryKeyChecked(B bean)throws RuntimeDaoException,ObjectRetrievalException{
return loadByPrimaryKeyChecked(checkNotNull(bean,"bean is null").primaryValues());
}
@Override
public final B loadByPrimaryKeyChecked(Object ...keys)throws RuntimeDaoException,ObjectRetrievalException{
// {{check parameters
if(metaData.primaryKeyNames.length == 0){
throw new UnsupportedOperationException();
}
String[] pkNames = metaData.primaryKeyNames;
int[] pkIds = metaData.primaryKeyIds;
if(hasNull(keys)){
throw new ObjectRetrievalException(new NullPointerException("primary key must not be null"));
}
checkArgument(keys.length == pkNames.length,
"INVALID ARGUMENT NUM, %s required",pkNames.length);
for(int i=0; i type = metaData.columnTypeOf(pkIds[i]);
checkArgument(type.isAssignableFrom(key.getClass()),
"INVALID type for pk %s,%s required",pkNames[i],type.getName());
}
// }}check parameters
return doLoadByPrimaryKeyChecked(keys);
}
protected B doLoadByPrimaryKeyChecked(Object ...keys)throws RuntimeDaoException,ObjectRetrievalException{
if(metaData.primaryKeyNames.length == 0){
throw new UnsupportedOperationException();
}
String[] pkNames = metaData.primaryKeyNames;
int[] pkIds = metaData.primaryKeyIds;
if(hasNull(keys)){
throw new ObjectRetrievalException(new NullPointerException("primary key must not be null"));
}
checkArgument(keys.length == pkNames.length,
"INVALID ARGUMENT NUM, %s required",pkNames.length);
for(int i=0; i type = metaData.columnTypeOf(pkIds[i]);
checkArgument(type.isAssignableFrom(key.getClass()),
"INVALID type for pk %s,%s required",pkNames[i],type.getName());
}
Connection c = null;
PreparedStatement ps = null;
try
{
c = this.getConnection();
StringBuilder sql = new StringBuilder("SELECT " + metaData.columnFields + " FROM " + metaData.tablename + " WHERE ");
sql.append(Joiner.on(" AND ").join(
Lists.transform(Arrays.asList(pkNames), SQL_FUN1)));
if(debug){
log("LOAD BY PK: " + sql.toString());
}
ps = c.prepareStatement(sql.toString(),
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
for(int i = 0; i pReturn = this.loadByPreparedStatementAsList(ps,null,1,-1);
if (1 == pReturn.size()) {
return pReturn.get(0);
} else {
throw new ObjectRetrievalException();
}
}
catch(ObjectRetrievalException e)
{
throw e;
}
catch(SQLException e)
{
throw new RuntimeDaoException(new DataRetrievalException(e));
}
finally
{
this.getManager().close(ps);
this.freeConnection(c);
}
}
@Override
public B loadByPrimaryKey(Object ...keys)throws RuntimeDaoException{
try{
return loadByPrimaryKeyChecked(keys);
}catch(ObjectRetrievalException e){
// not found
return null;
}
}
protected List loadByPks(Collection keys){
checkState(metaData.primaryKeyCount == 1,"UNSUPPORTED OPERATION");
if(null == keys){
return Collections.emptyList();
}
ArrayList list = new ArrayList(keys.size());
for(K key:keys){
list.add(loadByPrimaryKey(key));
}
return list;
}
@SuppressWarnings("unchecked")
protected List loadByPks(K... keys){
if(null == keys){
return Collections.emptyList();
}
return loadByPks(Arrays.asList(keys));
}
@Override
public boolean existsByPrimaryKey(B bean)throws RuntimeDaoException{
return null == bean ? false : existsPrimaryKey(bean.primaryValues());
}
@Override
public B checkDuplicate(B bean)throws RuntimeDaoException,ObjectRetrievalException{
if(existsByPrimaryKey(bean)){
throw new ObjectRetrievalException("Duplicate entry ("+ bean.primaryValues() +") for key 'PRIMARY'");
}
return bean;
}
@Override
public final boolean existsPrimaryKey(Object ...keys)throws RuntimeDaoException{
// {{check parameters
if(metaData.primaryKeyNames.length == 0){
throw new UnsupportedOperationException();
}
String[] pkNames = metaData.primaryKeyNames;
int[] pkIds = metaData.primaryKeyIds;
if(null == keys || hasNull(keys)){
return false;
}
checkArgument(keys.length == pkNames.length,
"INVALID ARGUMENT NUM, %s required",pkNames.length);
for(int i=0; i type = metaData.columnTypeOf(pkIds[i]);
checkArgument(type.isInstance(key),
"INVALID type for pk %s,%s required",pkNames[i],type.getName());
}
// }}check parameters
return doExistsPrimaryKey(keys);
}
protected boolean doExistsPrimaryKey(Object ...keys)throws RuntimeDaoException{
String[] pkNames = metaData.primaryKeyNames;
int[] pkIds = metaData.primaryKeyIds;
Connection c = null;
PreparedStatement ps = null;
try{
c = this.getConnection();
StringBuilder sql = new StringBuilder("SELECT COUNT(*) AS MCOUNT FROM " + metaData.tablename + " WHERE ");
sql.append(Joiner.on(" AND ").join(Lists.transform(Arrays.asList(pkNames),SQL_FUN1)));
if(debug){
log("ExistsPrimaryKey: " + sql);
}
ps = c.prepareStatement(sql.toString(),
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
for(int i = 0; iT checkDuplicateByPk(T primaryKeyValue)throws ObjectRetrievalException{
if(metaData.primaryKeyNames.length != 1){
throw new UnsupportedOperationException();
}
if(existsPrimaryKey(primaryKeyValue)){
throw new ObjectRetrievalException("Duplicate entry '"+ primaryKeyValue +"' for key 'PRIMARY'");
}
return primaryKeyValue;
}
@Override
public B[] loadByWhere(String where)throws RuntimeDaoException{
return this.loadByWhere(where, (int[])null);
}
@Override
public int loadByWhere(String where, TableManager.Action action)throws RuntimeDaoException{
return this.loadByWhere(where, null, action);
}
@Override
public B[] loadByWhere(String where, int[] fieldList)throws RuntimeDaoException{
return this.loadByWhere(where, fieldList, 1, -1);
}
@Override
public int loadByWhere(String where, int[] fieldList, TableManager.Action action)throws RuntimeDaoException{
return this.loadByWhere(where, fieldList, 1, -1, action);
}
@SuppressWarnings("unchecked")
@Override
public B[] loadByWhere(String where, int[] fieldList, int startRow, int numRows)throws RuntimeDaoException{
return this.loadByWhereAsList(where, fieldList, startRow, numRows).toArray((B[])Array.newInstance(metaData.beanType,0));
}
@Override
public int loadByWhere(String where, int[] fieldList, int startRow, int numRows,
TableManager.Action action)throws RuntimeDaoException{
return this.loadByWhereForAction(where, fieldList, startRow, numRows, action);
}
@Override
public List loadByWhereAsList(String where)throws RuntimeDaoException{
return this.loadByWhereAsList(where, null, 1, -1);
}
@Override
public List loadByWhereAsList(String where, int[] fieldList)throws RuntimeDaoException{
return this.loadByWhereAsList(where, fieldList, 1, -1);
}
@Override
public List loadByWhereAsList(String where, int[] fieldList, int startRow, int numRows)throws RuntimeDaoException{
ListAction action = new ListAction();
loadByWhereForAction(where, fieldList, startRow, numRows, action);
return action.getList();
}
@Override
public int loadByWhereForAction(String where, int[] fieldList, int startRow, int numRows,TableManager.Action action)throws RuntimeDaoException{
String sql=createSelectSql(fieldList, where);
return this.loadBySqlForAction(sql, null, fieldList, startRow, numRows, action);
}
@Override
public B[] loadUsingTemplate(B bean)throws RuntimeDaoException{
return this.loadUsingTemplate(bean, 1, -1, SEARCH_EXACT);
}
@Override
public int loadUsingTemplate(B bean, TableManager.Action action)throws RuntimeDaoException{
return this.loadUsingTemplate(bean, null, 1, -1, SEARCH_EXACT, action);
}
@Override
public B[] loadUsingTemplate(B bean, int startRow, int numRows)throws RuntimeDaoException{
return this.loadUsingTemplate(bean, startRow, numRows, SEARCH_EXACT);
}
@Override
public int loadUsingTemplate(B bean, int startRow, int numRows,
TableManager.Action action)throws RuntimeDaoException{
return this.loadUsingTemplate(bean, null, startRow, numRows,SEARCH_EXACT, action);
}
@SuppressWarnings("unchecked")
@Override
public B[] loadUsingTemplate(B bean, int startRow, int numRows, int searchType)throws RuntimeDaoException{
return this.loadUsingTemplateAsList(bean, startRow, numRows, searchType).toArray((B[])Array.newInstance(metaData.beanType,0));
}
@Override
public List loadUsingTemplateAsList(B bean)throws RuntimeDaoException{
return this.loadUsingTemplateAsList(bean, 1, -1, SEARCH_EXACT);
}
@Override
public List loadUsingTemplateAsList(B bean, int startRow, int numRows)throws RuntimeDaoException{
return this.loadUsingTemplateAsList(bean, startRow, numRows, SEARCH_EXACT);
}
@Override
public List loadUsingTemplateAsList(B bean, int startRow, int numRows, int searchType)throws RuntimeDaoException{
ListAction action = new ListAction();
loadUsingTemplate(bean,null,startRow,numRows,searchType, action);
return action.getList();
}
//18
@Override
public B loadUniqueUsingTemplate(B bean)
{
try {
return loadUniqueUsingTemplateChecked(bean);
} catch (ObjectRetrievalException e) {
return null;
}
}
//18-1
@Override
public B loadUniqueUsingTemplateChecked(B bean) throws ObjectRetrievalException
{
List beans = this.loadUsingTemplateAsList(bean);
switch(beans.size()){
case 0:
throw new ObjectRetrievalException("Not found element !!");
case 1:
return beans.get(0);
default:
throw new RuntimeDaoException(new ObjectRetrievalException("More than one element !!"));
}
}
@Override
public int loadUsingTemplate(B bean, int[] fieldList, int startRow, int numRows,int searchType, Action action)
{
StringBuilder sqlWhere = new StringBuilder("");
String sql=createSelectSql(fieldList,
this.fillWhere(sqlWhere, bean, searchType) > 0 ? " WHERE "+ sqlWhere.toString() : null);
PreparedStatement ps = null;
Connection connection = null;
try {
connection = this.getConnection();
if(debug){
log("loadUsingTemplate:" + sql);
}
ps = connection.prepareStatement(sql,
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
this.fillPreparedStatement(ps, bean, searchType,false);
return this.loadByPreparedStatement(ps, fieldList, startRow, numRows, action);
} catch (DaoException e) {
throw new RuntimeDaoException(e);
}catch (SQLException e) {
throw new RuntimeDaoException(new DataAccessException(e));
} finally {
this.getManager().close(ps);
this.freeConnection(connection);
}
}
/**
* @param bean type of foreign table
* @param left
* @param columnsMap
* @param startRow
* @param numRows
* @return list of B or empty list
*/
private
List loadByForeignKeyAsList(F left,MapcolumnsMap,int startRow, int numRows)
{
if(null == left){
return Collections.emptyList();
}
checkArgument(columnsMap != null && !columnsMap.isEmpty(),"columnsMap is null or empty");
B bean = createBean().copy(left, columnsMap);
return loadUsingTemplateAsList(bean,startRow,numRows);
}
/**
* @param fkName
* @param left
* @param startRow
* @param numRows
* @param bean type of foreign table
* @return list of B or empty list
*/
protected
List loadByForeignKeyAsList(String fkName,F left,int startRow, int numRows)
{
ImmutableBiMap columnsMap = metaData.foreignKeyIdMapOf(fkName).inverse();
return loadByForeignKeyAsList(left,columnsMap,startRow,numRows);
}
@Override
public B save(B bean)throws RuntimeDaoException{
if(null != bean){
if (bean.isNew()) {
this.insert(bean);
} else {
this.update(bean);
}
}
return bean;
}
@Override
public B[] save(B[] beans)throws RuntimeDaoException{
if(null != beans){
for (B bean : beans)
{
this.save(bean);
}
}
return beans;
}
@Override
public > C save(C beans)throws RuntimeDaoException{
if(null != beans){
for (B bean : beans)
{
this.save(bean);
}
}
return beans;
}
@Override
public > C saveAsTransaction(final C beans)throws RuntimeDaoException{
return this.runAsTransaction(new Callable(){
@Override
public C call() throws Exception {
return save(beans);
}});
}
@Override
public B[] saveAsTransaction(final B[] beans)throws RuntimeDaoException{
return this.runAsTransaction(new Callable(){
@Override
public B[] call() throws Exception {
return save(beans);
}});
}
private String checkWhere(String where){
where = MoreObjects.firstNonNull(where, "").trim();
checkArgument(where.isEmpty() || where.toUpperCase().startsWith("WHERE "),
"WHERE expression must start with 'WHERE'(case insensitive)");
return where;
}
@SuppressWarnings("unchecked")
@Override
public List loadColumnAsList(String column,boolean distinct,String where,int startRow,int numRows)throws RuntimeDaoException{
int columnId = metaData.columnIDOf(column);
checkArgument(columnId>=0,"INVALID column name %s",column);
String sql = String.format("SELECT %s " + metaData.columnNameOf(columnId) + " FROM %s %s",
distinct ? "DISTINCT" : "",
metaData.tablename,
checkWhere(where));
return runSqlAsList((Class)metaData.columnTypes.get(columnId), sql);
}
/**
* generate SQL query(SELECT) statement,such as: 'SELECT id,name from mytable WHERE id=1'
* @param fieldList
* @param where where condition expression statement that start with 'WHERE',or {@code null},or empty string
* @return SQL statement string
* @throws IllegalArgumentException where condition expression don't start with 'WHERE'
*/
private String createSelectSql(int[] fieldList, String where){
StringBuffer sql = new StringBuffer(128);
String fullFields = metaData.columnFullFields;
if(null == fieldList || 0 == fieldList.length) {
sql.append("SELECT ").append(fullFields);
} else{
sql.append("SELECT ");
String[] names=fullFields.split(",");
for(int i = 0; i < fieldList.length; ++i){
if(i > 0) {
sql.append(",");
}
sql.append(names[fieldList[i]]);
}
}
sql.append(" FROM " + this.metaData.tablename + " ");
sql.append(checkWhere(where));
return sql.toString();
}
//2.2
@Override
public int delete(B bean){
if(metaData.primaryKeyNames.length==0){
throw new UnsupportedOperationException();
}
if(hasNullPk(bean)){
return 0;
}
Connection c = null;
PreparedStatement ps = null;
String [] pks = metaData.primaryKeyNames;
int[] pkIds = metaData.primaryKeyIds;
try
{
// listener callback
getListenerContainer().beforeDelete(bean);
c = this.getConnection();
StringBuilder sql = new StringBuilder("DELETE FROM " + metaData.tablename +" WHERE ");
sql.append(Joiner.on(" AND ").join(Lists.transform(Arrays.asList(pks),SQL_FUN1)));
if(debug){
log("deleteByPrimaryKey: " + sql);
}
ps = c.prepareStatement(sql.toString(),
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
for(int i = 0; i0){
// listener callback
getListenerContainer().afterDelete(bean);
}
return rows;
}
catch(SQLException e)
{
throw new RuntimeDaoException(new DataAccessException(e));
}
finally
{
// listener callback
getListenerContainer().done();
getManager().close(ps);
freeConnection(c);
}
}
protected int deleteByPks(Collection keys){
checkState(metaData.primaryKeyCount == 1,"UNSUPPORTED OPERATION");
int count = 0;
if(null != keys){
for(K key :keys){
count += deleteByPrimaryKey(key);
}
}
return count;
}
@SuppressWarnings("unchecked")
protected int deleteByPks(K... keys){
if(null == keys){
return 0;
}
return deleteByPks(Arrays.asList(keys));
}
@Override
public int deleteByPrimaryKey(Object ...keys)throws RuntimeDaoException{
if(hasNull(keys)){
return 0;
}
checkArgument(keys.length == metaData.primaryKeyCount,"argument number mismatch with primary key number");
return delete(createBean(keys));
}
//2.4
@SuppressWarnings("unchecked")
@Override
public int delete(B... beans)throws RuntimeDaoException{
int count = 0;
if(null != beans){
for(B bean :beans){
count += delete(bean);
}
}
return count;
}
//2.5
@Override
public int delete(Collection beans)throws RuntimeDaoException{
int count = 0;
if(null != beans){
for(B bean :beans){
count += delete(bean);
}
}
return count;
}
/**
* Retrieves the T object referenced by fkName.
* @param fkName foreign key name.
* @param bean the B object to use
* @param
* @return the associated T bean or {@code null} if {@code bean} is {@code null}
* @throws RuntimeDaoException
*/
protected T getReferencedBean(String fkName, B bean) throws RuntimeDaoException{
if(null == bean){
return null;
}
ForeignKeyMetaData foreignkey = checkNotNull(metaData.foreignKeys.get(fkName),
"INVALID fkName %s for table %s", fkName, metaData.tablename);
BaseTableManager foreignManager = baseManagerOf(foreignkey.foreignTable);
T t = foreignManager.createBean().copy(bean, metaData.foreignKeyIdMapOf(fkName));
return foreignManager.loadByPrimaryKey(t);
}
/**
* Associates the B object to the T object by fkName field.
* @param fkName see also {@link #getReferencedBean(String, BaseBean)}
* @param bean the B object to use
* @param beanToSet the T object to associate to the B bean
* @param see also {@link #getReferencedBean(String, BaseBean)}
* @return always beanToSet saved
* @throws RuntimeDaoException
*/
protected T setReferencedBean(String fkName, B bean, T beanToSet) throws RuntimeDaoException{
if(null != bean){
ForeignKeyMetaData foreignkey = checkNotNull(metaData.foreignKeys.get(fkName),
"INVALID fkName %s for table %s", fkName, metaData.tablename);
TableManager foreignManager = managerOf(foreignkey.foreignTable);
foreignManager.save(beanToSet);
bean.copy(beanToSet, metaData.foreignKeyIdMapOf(fkName).inverse());
}
return beanToSet;
}
/**
* Retrieves imported T objects by fkIndex.
* @param
* @param fkName foreign key name.
* @param bean the B object to use
* @return the associated T beans or {@code null} if {@code bean} is {@code null}
* @throws RuntimeDaoException
*/
@SuppressWarnings("unchecked")
protected T[] getImportedBeans(String fkName,B bean) throws RuntimeDaoException{
ForeignKeyMetaData foreignkey =metaData.getImportedKey(fkName);
RowMetaData foreignMetaData = RowMetaData.getMetaData(foreignkey.foreignTable);
return getImportedBeansAsList(fkName,bean).toArray((T[]) Array.newInstance(foreignMetaData.beanType, 0));
}
/**
* Retrieves imported T objects by fkIndex.
* @param
* @param fkName foreign key name.
* @param bean the B object to use
* @return the associated T beans or {@code null} if {@code bean} is {@code null}
* @throws RuntimeDaoException
*/
protected List getImportedBeansAsList(String fkName, B bean,int startRow, int numRows)throws RuntimeDaoException{
ForeignKeyMetaData foreignkey =metaData.getImportedKey(fkName);
TableManager foreignManager = managerOf(foreignkey.ownerTable);
return baseManagerOf(foreignManager).loadByForeignKeyAsList(fkName,bean, startRow, numRows);
}
/**
* Retrieves imported T objects by ikIndex.
* @param fkname foreign key name.see also {@link #getImportedBeans(String, BaseBean)}
* @param bean the B object to use
* @param see also {@link #getImportedBeans(String, BaseBean)}
* @return the associated T beans or {@code null} if {@code bean} is {@code null}
* @throws RuntimeDaoException
*/
protected List getImportedBeansAsList(String fkName, B bean)throws RuntimeDaoException{
return getImportedBeansAsList(fkName,bean, 1, -1);
}
protected List getImportedBeansAsList(String fkName,Object...keys) throws RuntimeDaoException{
return getImportedBeansAsList(fkName,createBean(keys),1,-1);
}
@SuppressWarnings("unchecked")
protected T[] getImportedBeans(String fkName,Object...keys) throws RuntimeDaoException{
ForeignKeyMetaData foreignkey =metaData.getImportedKey(fkName);
RowMetaData foreignMetaData = RowMetaData.getMetaData(foreignkey.foreignTable);
return getImportedBeansAsList(fkName,keys).toArray((T[]) Array.newInstance(foreignMetaData.beanType, 0));
}
/**
* Set the importedBeans associates to the bean by fkIndex
* @param fkName foreign key name. see also {@link #getImportedBeans(String, BaseBean)}
* @param bean the bean object to use
* @param importedBeans the T object to associate to bean
*
* @param see also {@link #getImportedBeans(String, BaseBean)}
* @return importedBeans always
* @throws RuntimeDaoException
*/
protected > C setImportedBeans(String fkName, B bean,
C importedBeans)throws RuntimeDaoException{
if(null != importedBeans){
ForeignKeyMetaData foreignkey =metaData.getImportedKey(fkName);
BaseTableManager foreignManager = baseManagerOf(foreignkey.ownerTable);
for( T importBean : importedBeans ){
foreignManager.setReferencedBean(fkName , importBean,bean);
}
}
return importedBeans;
}
/**
* Set the importedBeans associates to the bean by {@code ikIndex}
* @param fkName foreign key name.see also {@link #getImportedBeans(String, BaseBean)}
* @param bean the bean object to use
* @param importedBeans the T object to associate to bean
*
* @param see also {@link #getImportedBeans(String, BaseBean)}
* @return importedBeans always
* @throws RuntimeDaoException
*/
protected T[] setImportedBeans(String fkName, B bean, T[] importedBeans) throws RuntimeDaoException{
if(null != importedBeans){
setImportedBeans(fkName, bean, Arrays.asList(importedBeans));
}
return importedBeans;
}
/**
* delete all imported beans by fkName
* @param fkName foreign key name
* @param bean
* @return deleted row count or 0 if bean is null
*/
protected int deleteImportedBeans(String fkName,B bean) throws RuntimeDaoException{
if(bean == null){
return 0;
}
ForeignKeyMetaData foreignkey = metaData.getImportedKey(fkName);
RowMetaData foreignMetaData = RowMetaData.getMetaData(foreignkey.foreignTable);
BaseTableManager foreignManager = baseManagerOf(foreignkey.ownerTable);
BaseBean tmpl = foreignManager.createBean().copy(bean,foreignMetaData.foreignKeyIdMapOf(fkName));
return foreignManager.deleteUsingTemplate(tmpl);
}
protected int deleteImportedBeans(String fkName, Map idValueMap) throws RuntimeDaoException{
B bean = createBean().copy(idValueMap);
return deleteImportedBeans(fkName,bean);
}
protected int deleteImportedBeans(String fkName,Object...keys) throws RuntimeDaoException{
return deleteImportedBeans(fkName,createBean(keys));
}
private Map makeIndexValueMap(String indexName,Object ...indexValues){
IndexMetaData indexMetaData = metaData.indices.get(indexName);
checkArgument(null != indexMetaData,"INVALID index name %s",indexName);
checkArgument(null != indexValues && indexValues.length == indexMetaData.columns.size(),"INVALID index value");
ImmutableMap.Builder builder = ImmutableMap.builder();
for(int i=0;i columnType = metaData.columnTypeOf(column);
checkArgument(columnType.isInstance(value),
"INVALID value type %s for %s,%s required",value.getClass(),column,columnType);
builder.put(metaData.columnIDOf(column), value);
}
}
return builder.build();
}
//_____________________________________________________________________
//
// USING INDICES
//_____________________________________________________________________
/**
* Retrieves a array of B bean using the index specified by keyIndex.
* @param indexName name of index
* @param indexValues key values of index
* @return B array
* @throws RuntimeDaoException
* @see #loadByIndexAsList(int ,Object ...)
*/
@SuppressWarnings("unchecked")
protected B[] loadByIndex(String indexName,Object ...keys)throws RuntimeDaoException{
return this.loadByIndexAsList(indexName,keys).toArray((B[])Array.newInstance(metaData.beanType,0));
}
/**
* Retrieves a list of B bean using the index specified by indexName.
* @param indexName name of index
* @param indexValues key values of index
* @return a list of B bean
* @throws RuntimeDaoException
*/
protected List loadByIndexAsList(String indexName,Object ...indexValues)throws RuntimeDaoException{
Map map = makeIndexValueMap(indexName,indexValues);
return loadUsingTemplateAsList(map);
}
protected final B loadUniqueByIndex(String indexName,Object ...indexValues)throws RuntimeDaoException{
return doLoadUniqueByIndex(indexName, indexValues);
}
protected B doLoadUniqueByIndex(String indexName,Object ...indexValues)throws RuntimeDaoException{
Map keys = makeIndexValueMap(indexName,indexValues);
B bean = createBean().copy(keys);
return loadUniqueUsingTemplate(bean);
}
protected final B loadUniqueByIndexChecked(String indexName,Object ...indexValues)throws ObjectRetrievalException{
if(hasNull(indexValues)){
throw new ObjectRetrievalException(new NullPointerException("index keys must not be null"));
}
return doLoadUniqueByIndexChecked(indexName, indexValues);
}
protected B doLoadUniqueByIndexChecked(String indexName,Object ...indexValues)throws ObjectRetrievalException{
Map keys = makeIndexValueMap(indexName,indexValues);
B bean = createBean().copy(keys);
return loadUniqueUsingTemplateChecked(bean);
}
/**
* Deletes rows using key.
* @param indexName name of index
* @param indexValues key values of index
* @return the number of deleted objects
* @throws RuntimeDaoException
*/
protected int deleteByIndex(String indexName,Object ...indexValues)throws RuntimeDaoException{
Map map = makeIndexValueMap(indexName,indexValues);
return deleteUsingTemplate(map);
}
@SuppressWarnings("unchecked")
protected List loadViaJunctionAsList(String junctionTable,T linked, int startRow, int numRows){
BaseTableManager manager = baseManagerOf(junctionTable);
return manager.loadViaJunctionTableAsList((Class)metaData.beanType, linked, startRow, numRows);
}
protected List loadViaJunctionAsList(String junctionTable,T linked){
return loadViaJunctionAsList(junctionTable,linked,1,-1);
}
private List loadUsingTemplateAsList(Mapkeys){
B bean = createBean().copy(keys);
return loadUsingTemplateAsList(bean);
}
private int deleteUsingTemplate(Mapkeys){
B bean = createBean().copy(keys);
return deleteUsingTemplate(bean);
}
protected List loadByIndexForIndices(String indexName,Collection indexs)
{
IndexMetaData indexMetaData = metaData.indices.get(indexName);
checkArgument(null != indexMetaData,"INVALID index name %s",indexName);
checkArgument(1 == indexMetaData.columns.size(),"column count of index must be 1");
checkArgument(indexMetaData.unique,"index must be unique");
if(null == indexs ){
return Collections.emptyList();
}
List list = new ArrayList(indexs.size());
for(T key: indexs){
list.add(loadUniqueByIndex(indexName,key));
}
return list;
}
@SuppressWarnings("unchecked")
protected List loadByIndexForIndices(String indexName,T... indexs){
if(indexs == null || indexs.length == 0){
return Collections.emptyList();
}
return loadByIndexForIndices(indexName,Arrays.asList(indexs));
}
protected int deleteByIndexForIndices(String indexName,Collection indexs)
{
IndexMetaData indexMetaData = metaData.indices.get(indexName);
checkArgument(null != indexMetaData,"INVALID index name %s",indexName);
checkArgument(1 == indexMetaData.columns.size(),"column count of index must be 1");
if(null == indexs ){
return 0;
}
int count = 0;
for(T key: indexs){
if(key != null){
count += deleteByIndex(indexName, key);
}
}
return count;
}
@SuppressWarnings("unchecked")
protected int deleteByIndexForIndices(String indexName,T... indexs){
if(indexs == null || indexs.length == 0){
return 0;
}
return deleteByIndexForIndices(indexName,Arrays.asList(indexs));
}
private boolean checkPkValid(B bean)
{
if(metaData.primaryKeyNames.length ==0){
return false;
}
for(String name:metaData.primaryKeyNames){
if(! (bean.isInitialized(name) && null != bean.getValue(name))){
return false;
}
}
return true;
}
//21
@Override
public int deleteUsingTemplate(B bean)
{
if(bean == null || !bean.isModified()){
return 0;
}
if(checkPkValid(bean)){
return this.deleteByPrimaryKey(bean);
}
if( !getListenerContainer().isEmpty()){
DeleteBeanAction action=new DeleteBeanAction();
this.loadUsingTemplate(bean,action);
return action.getCount();
}
Connection c = null;
PreparedStatement ps = null;
StringBuilder sql = new StringBuilder("DELETE FROM " + metaData.tablename +" ");
StringBuilder sqlWhere = new StringBuilder("");
try
{
fillWhere(sqlWhere, bean, SEARCH_EXACT);
sql.append(" WHERE ").append(sqlWhere);
c = this.getConnection();
if(debug){
log("deleteUsingTemplate: " + sql.toString());
}
ps = c.prepareStatement(sql.toString(),
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
this.fillPreparedStatement(ps, bean, SEARCH_EXACT, false);
return ps.executeUpdate();
}
catch(SQLException e)
{
throw new RuntimeDaoException(new DataAccessException(e));
}
finally
{
this.getManager().close(ps);
this.freeConnection(c);
}
}
//11
@Override
public int deleteByWhere(String where)
{
if( !getListenerContainer().isEmpty()){
final DeleteBeanAction action = new DeleteBeanAction();
this.loadByWhere(where,action);
return action.getCount();
}
Connection c = null;
PreparedStatement ps = null;
try
{
c = this.getConnection();
StringBuilder sql = new StringBuilder("DELETE FROM " + metaData.tablename + " " + where);
if(debug){
log("deleteByWhere: " + sql);
}
ps = c.prepareStatement(sql.toString());
return ps.executeUpdate();
}
catch(SQLException e)
{
throw new RuntimeDaoException(new DataAccessException(e));
}
finally
{
this.getManager().close(ps);
this.freeConnection(c);
}
}
private void setPreparedStatement(PreparedStatement ps,int pos,Object value,int columnId)
throws SQLException {
Manager.fillPreparedStatement(ps, pos, value, metaData.sqlTypes[columnId]);
}
private void setPreparedStatement(PreparedStatement ps,int pos,B bean,int columnId)
throws SQLException {
setPreparedStatement(ps,pos,bean.getValue(columnId),columnId);
}
//3.9 SYNC SAVE
/**
* Save the B bean and referenced beans and imported beans into the database.
*
* @param bean the B bean to be saved
* @param referenceBeans referenced beans beans
* @param importedBeans imported beans
* @return the inserted or updated B bean
* @throws RuntimeDaoException
*/
private B save(B bean,Map referenceBeans,Map> importedBeans) throws RuntimeDaoException{
if(null == bean){
return null;
}
referenceBeans = MoreObjects.firstNonNull(referenceBeans, Collections.emptyMap());
importedBeans = MoreObjects.firstNonNull(importedBeans, Collections.>emptyMap());
for(Entry entry:referenceBeans.entrySet()){
BaseBean beanToSet = entry.getValue();
if(beanToSet != null){
setReferencedBean(entry.getKey(), bean, beanToSet);
}
}
bean = this.save( bean );
for(Entry> entry:importedBeans.entrySet()){
String ikName = entry.getKey();
Collection importeds = entry.getValue();
if(null != importeds){
setImportedBeans(ikName, bean, importeds);
TableManager impManager = managerOf(metaData.getImportedKey(ikName).ownerTable);
impManager.save(importeds);
}
}
return bean;
}
@SuppressWarnings("unchecked")
protected B saveFully(B bean,Object[] args){
HashMap referenced = Maps.newHashMap();
int index = 0;
for(String key : metaData.foreignKeys.keySet()){
referenced.put(key, (BaseBean) args[index++]);
}
HashMap> imported = Maps.newHashMap();
for(ForeignKeyMetaData key : metaData.getImportedKeys()){
Object obj = args[index++];
if(obj == null){
imported.put(key.name, null);
}else if(obj.getClass().isArray()){
checkArgument(BaseBean.class.isAssignableFrom(obj.getClass().getComponentType()),
"INVALID COMPONENT TYPE FOR %s",key.name);
imported.put(key.name,Arrays.asList((BaseBean[])obj));
}else if(obj instanceof Collection){
imported.put(key.name,(Collection)obj);
}else{
throw new IllegalArgumentException(logString("INVALID TYPE %s FOR %s",obj.getClass(),key.name));
}
}
return save(bean,referenced,imported);
}
protected B saveFullyAsTransaction(final B bean, final Object[] args){
return this.runAsTransaction(new Callable(){
@Override
public B call() throws Exception {
return saveFully(bean , args );
}});
}
//_____________________________________________________________________
//
// COUNT
//_____________________________________________________________________
//25
@Override
public int countWhere(String where)
{
String sql = new StringBuffer("SELECT COUNT(*) AS MCOUNT FROM " + metaData.tablename + " ")
.append(null == where ? "" : where).toString();
return runSqlForValue(Long.class, sql).intValue();
}
//20
/**
* count the number of elements of a specific B bean given the search type
*
* @param bean the B template to look for
* @param searchType exact ? like ? starting like ?
* @return the number of rows returned
*/
@Override
public int countUsingTemplate(B bean, int searchType)
{
Connection c = null;
PreparedStatement ps = null;
StringBuilder sql = new StringBuilder("SELECT COUNT(*) AS MCOUNT FROM " + metaData.tablename);
StringBuilder sqlWhere = new StringBuilder("");
try
{
if (this.fillWhere(sqlWhere, bean, SEARCH_EXACT) > 0) {
sql.append(" WHERE ").append(sqlWhere);
} else {
if(debug){
log("The bean to look is not initialized... counting all...");
}
}
c = this.getConnection();
if(debug){
log("countUsingTemplate: " + sql.toString());
}
ps = c.prepareStatement(sql.toString(),
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
this.fillPreparedStatement(ps, bean, searchType,false);
return getManager().runPreparedStatementForValue(Long.class,ps).intValue();
}
catch(SQLException e)
{
throw new RuntimeDaoException(new DataAccessException(e));
}
finally
{
this.getManager().close(ps);
this.freeConnection(c);
sql = null;
sqlWhere = null;
}
}
/**
* fills the given StringBuilder with the sql where clauses constructed using the bean and the search type
* @param sqlWhere the StringBuilder that will be filled
* @param bean the bean to use for creating the where clauses
* @param searchType exact ? like ? starting like ?
* @return the number of clauses returned
*/
private int fillWhere(StringBuilder sqlWhere, B bean, int searchType)
{
if (bean == null) {
return 0;
}
int dirtyCount = 0;
String sqlEqualsOperation = searchType == SEARCH_EXACT ? "=" : " like ";
List fields = metaData.columnNames;
for(int i=0; i fields = metaData.columnNames;
for(int columnId=0; columnId{
private final BaseBean bean;
public FieldNullChecker(BaseBean bean) {
this.bean = checkNotNull(bean,"bean is null");
}
@Override
public boolean apply(String input) {
return null == bean.getValue(input);
}
}
//_____________________________________________________________________
//
// MANY TO MANY: LOAD OTHER BEAN VIA JUNCTION TABLE
//_____________________________________________________________________
/**
* Retrieves an list of R using the junction table junction table(junctionTablename), given a linked table bean,
* specifying the start row and the number of rows.
* @param rightType
* @param left
* @param startRow the start row to be used (first row = 1, last row = -1)
* @param numRows the number of rows to be retrieved (all rows = a negative number)
* @param
* @param
* @return a list of R
*/
public
List loadViaJunctionTableAsList( Class rightType,L left, int startRow, int numRows)
{
checkState(!metaData.getJunctionTablePkMap().isEmpty(),"%s is not junction table",metaData.tablename);
checkArgument(null != left,"left is null");
checkArgument(null != rightType,"rightType is null");
RowMetaData leftMetaData = RowMetaData.getMetaData(left.tableName());
RowMetaData rightMetaData = RowMetaData.getMetaData(rightType);
checkArgument(!leftMetaData.equals(rightMetaData),"same metadata FOR left AND right type");
checkArgument(metaData.isLinkedTable(leftMetaData.tablename),"INVALID left type %s",left.getClass());
checkArgument(metaData.isLinkedTable(rightMetaData.tablename),"INVALID right type %s",rightType);
Map leftFields = metaData.junctionMapOf(leftMetaData.tablename);
Map rightFields = metaData.junctionMapOf(rightMetaData.tablename);
if(Iterables.tryFind(leftFields.values(), new FieldNullChecker(left)).isPresent()){
return Collections.emptyList();
}
Connection c = null;
PreparedStatement ps = null;
String sql = " SELECT " + rightMetaData.columnFullFields
+ " FROM "+ metaData.tablename + ", " + rightMetaData.tablename
+ " WHERE "
+ Joiner.on(" AND ").join(Iterables.transform(leftFields.keySet(),
new Function(){
@Override
public String apply(String input) {
return input + "=?";
}}))
+ " AND "
+ Joiner.on(" AND ").join(Iterables.transform(rightFields.entrySet(),
new Function,String>(){
@Override
public String apply(Map.Entry input) {
return input.getKey() + "=" + input.getValue();
}}));
try
{
c = this.getConnection();
if(debug){
log("loadViaJunctionTableAsList" + sql);
}
ps = c.prepareStatement(sql,
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
int pos =1;
for(String key:leftFields.keySet()){
String fullName = leftFields.get(key);
int columnId = leftMetaData.columnIDOf(fullName);
Manager.fillPreparedStatement(ps, pos++,
left.getValue(columnId),
leftMetaData.sqlTypeOf(columnId));
}
BaseTableManager rightManager = baseManagerOf(rightMetaData.tablename);
return rightManager.loadByPreparedStatementAsList(ps, null, startRow, numRows);
}
catch (SQLException e)
{
throw new RuntimeDaoException(new DaoException(e.getMessage(), e));
}
finally
{
this.getManager().close(ps);
this.freeConnection(c);
}
}
private B makeJunctionBean(L left,R right){
Map map = metaData.getJunctionTablePkMap();
checkState(!map.isEmpty(), "%s is not junction table",metaData.tablename);
Object[] primaryValues = new Object[metaData.primaryKeyCount];
for(int i=0; i
* @param
* @param left
* @param right
*/
private
void addJunction(L left,R right){
if(hasNullPk(left) || hasNullPk(right)){
return ;
}
B junction = makeJunctionBean(left,right);
if(!existsByPrimaryKey(junction)){
save(junction);
}
}
/**
* remove junction between L and R
* @param
* @param
* @param left
* @param right
*/
private
int deleteJunction(L left,R right){
if(hasNullPk(left) || hasNullPk(right)){
return 0;
}
B junction = makeJunctionBean(left,right);
return delete(junction);
}
@SuppressWarnings("unchecked")
private
void addJunction(L left,R... rights){
if(null != rights){
for(R linked:rights){
addJunction(left,linked);
}
}
}
private
void addJunction(L left,Collection rights){
if(null != rights){
for(R linked:rights){
addJunction(left,linked);
}
}
}
@SuppressWarnings("unchecked")
private
int deleteJunction(L left,R... rights){
if(null != rights){
return deleteJunction(left, Arrays.asList(rights));
}
return 0;
}
private
int deleteJunction(L left,Collection rights){
int count = 0;
if(null != rights){
for(R right:rights){
count += deleteJunction(left,right);
}
}
return count;
}
/**
* add junction between B and R if junction not exists
* @param
* @param junction junction table name
* @param bean
* @param linked
*/
protected
void addJunction(String junction,B bean,R linked){
baseManagerOf(junction).addJunction(bean,linked);
}
/**
* add junction between B and R if junction not exists
* @param
* @param junction junction table name
* @param bean
* @param linkedBeans
*/
@SuppressWarnings({ "unchecked" })
protected
void addJunction(String junction,B bean,R... linkedBeans){
baseManagerOf(junction).addJunction(bean,linkedBeans);
}
/**
* add junction between B and R if junction not exists
* @param
* @param junction junction table name
* @param bean
* @param linkedBeans
*/
@SuppressWarnings({ })
protected
void addJunction(String junction,B bean,Collection linkedBeans){
baseManagerOf(junction).addJunction(bean,linkedBeans);
}
/**
* delete junction between B and R
* @param
* @param junction junction table name
* @param bean
* @param linked
* @return count of deleted rows
*/
protected
int deleteJunction(String junction,B bean,R linked){
return baseManagerOf(junction).deleteJunction(bean,linked);
}
/**
* delete junction between B and R
* @param
* @param junction junction table name
* @param bean
* @param linkedBeans
* @return count of deleted rows
*/
@SuppressWarnings({ "unchecked" })
protected
int deleteJunction(String junction,B bean,R... linkedBeans){
return baseManagerOf(junction).deleteJunction(bean,linkedBeans);
}
/**
* delete junction between B and R
* @param
* @param junction junction table name
* @param bean
* @param linkedBeans
* @return count of deleted rows
*/
protected
int deleteJunction(String junction,B bean,Collection linkedBeans){
return baseManagerOf(junction).deleteJunction(bean,linkedBeans);
}
//28-2
/** decode a resultset and call action
* @param rs the resultset to decode
* @param fieldList table of the field's associated constants
* @param startRow the start row to be used (first row = 1, last row = -1)
* @param numRows the number of rows to be retrieved (all rows = a negative number)
* @param action interface obj for do something
* @return the count dealt by action
* @throws IllegalArgumentException
*/
protected int actionOnResultSet(ResultSet rs, int[] fieldList, int startRow, int numRows, Action action) throws DaoException{
try{
int count = 0;
if(0!=numRows){
checkArgument(startRow>=1,"invalid argument:startRow (must >=1)");
checkArgument(null !=action && null != rs,"invalid argument:action OR rs (must not be null)");
rs.absolute(startRow - 1);
if (fieldList == null) {
if(numRows<0){
for(;rs.next();++count){
action.call(decodeRow(rs));
}
}else{
for(;rs.next() && count T getColumnValue(ResultSet resultSet, int columnId) throws SQLException {
return (T) Manager.getObject(resultSet,columnId + 1, metaData.columnTypeOf(columnId));
}
protected void setColumnValue(B bean,int columnId, Object value ){
if(value instanceof byte[] && ByteBuffer.class.equals(metaData.columnTypeOf(columnId))){
value = ByteBuffer.wrap((byte[])value);
}
bean.setValue(columnId, value);
}
//29
/**
* Transforms a ResultSet iterating on a B bean.
*
* @param rs the ResultSet to be transformed
* @return bean resulting B bean
*/
private B decodeRow(ResultSet rs) throws DaoException
{
B bean = createBean();
try
{
for(int i=0; i=0 && fieldList[i] loadByPreparedStatementAsList(PreparedStatement ps, int[] fieldList, int startRow, int numRows) throws DaoException
{
ListAction action = new ListAction();
loadByPreparedStatement(ps,fieldList,startRow,numRows,action);
return action.getList();
}
//34-2
/**
* Loads each element using a prepared statement specifying a list of fields to be retrieved,
* and specifying the start row and the number of rows
* and dealt by action.
*
* @param ps the PreparedStatement to be used
* @param startRow the start row to be used (first row = 1, last row = -1)
* @param numRows the number of rows to be retrieved (all rows = a negative number)
* @param fieldList table of the field's associated constants
* @param action Action object for do something(not null)
* @return the count dealt by action
*/
private int loadByPreparedStatement(PreparedStatement ps, int[] fieldList, int startRow, int numRows,Action action) throws DaoException
{
ResultSet rs = null;
try {
ps.setFetchSize(100);
rs = ps.executeQuery();
return this.actionOnResultSet(rs, fieldList, startRow, numRows, action);
} catch (DaoException e) {
throw e;
} catch (SQLException e) {
throw new DataAccessException(e);
} finally {
this.getManager().close(rs);
}
}
//_____________________________________________________________________
//
// LISTENER
//_____________________________________________________________________
/** foreign key listener for DEELTE RULE */
protected class DeleteRuleListener extends BaseForeignKeyListener{
protected final String fkName;
private final ForeignKeyRule deleteRule;
private final int[] foreignKeyIds;
DeleteRuleListener(String fkName) {
checkArgument(metaData.foreignKeys.containsKey(fkName),"INVALID foreign key name %s",fkName);
this.fkName = fkName;
this.deleteRule = metaData.foreignKeys.get(fkName).deleteRule;
this.foreignKeyIds = metaData.foreignKeyIdArrayOf(fkName);
}
@Override
protected List getImportedBeans(F bean) {
return getListenerContainer().isEmpty()
? Collections.emptyList()
: loadByForeignKeyAsList(fkName,bean,1,-1);
}
@SuppressWarnings("unchecked")
@Override
protected void onRemove(List affectedBeans) throws DaoException {
switch (deleteRule) {
case SET_NULL:
for(B bean : affectedBeans){
BaseRow updated = ((BaseRow)bean).clone();
for(int i=0; i container = getListenerContainer();
switch(event){
case UPDATE:
container.beforeUpdate(bean);
container.afterUpdate(bean);
container.done();
break;
case DELETE:
container.beforeDelete(bean);
container.afterDelete(bean);
container.done();
break;
case INSERT:
// DO NOTHING
// container.beforeInsert(bean);
// container.afterInsert(bean);
// container.done();
default:
break;
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("DeleteRuleListener [fkName=");
builder.append(fkName);
builder.append(", deleteRule=");
builder.append(deleteRule);
builder.append(", foreignKeyIds=");
builder.append(metaData.columnNamesOf(foreignKeyIds));
builder.append("]");
return builder.toString();
}
}
//35
@Override
public void registerListener(TableListener listener)
{
this.getListenerContainer().add(listener);
if(debug){
log("REGISTER TABLE LISTENER {}",listener);
}
}
//36
@Override
public void unregisterListener(TableListener listener)
{
this.getListenerContainer().remove(listener);
if(debug){
log("UNREGISTER TABLE LISTENER {}",listener);
}
}
//37-2
/**
* bind foreign key listener to foreign table:
* DELETE RULE
*/
public void bindForeignKeyListenerForDeleteRule(){
for(Entry> entry : getForeignKeyDeleteListeners().entrySet()){
TableManager manager = managerOf(metaData.foreignKeys.get(entry.getKey()).foreignTable);
manager.registerListener(entry.getValue());
}
}
//37-3
/**
* unbind foreign key listener from all of foreign tables
* @see #bindForeignKeyListenerForDeleteRule()
*/
public void unbindForeignKeyListenerForDeleteRule(){
for(Entry> entry : getForeignKeyDeleteListeners().entrySet()){
TableManager manager = managerOf(metaData.foreignKeys.get(entry.getKey()).foreignTable);
manager.unregisterListener(entry.getValue());
}
}
//_____________________________________________________________________
//
// UTILS
//_____________________________________________________________________
//40
/**
* Retrieves the manager object used to get connections.
*
* @return the manager used
*/
protected Manager getManager()
{
return Manager.getInstance();
}
//41
/**
* Frees the connection.
*
* @param c the connection to release
*/
protected void freeConnection(Connection c)
{
// back to pool
this.getManager().releaseConnection(c);
}
//42
/**
* Gets the connection.
*/
protected Connection getConnection() throws DaoException
{
try
{
return this.getManager().getConnection();
}
catch(SQLException e)
{
throw new DataAccessException(e);
}
}
private int loadBySqlForAction(String sql, Object[] argList, int[] fieldList,int startRow, int numRows,Action action){
PreparedStatement ps = null;
Connection connection = null;
try {
connection = this.getConnection();
if(debug){
log("loadBySqlForAction:" + sql );
}
ps = connection.prepareStatement(sql,
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
Manager.fillPrepareStatement(ps, argList);
return this.loadByPreparedStatement(ps, fieldList, startRow, numRows, action);
} catch (SQLException e) {
throw new RuntimeDaoException(new DataAccessException(e));
} finally {
this.getManager().close(ps);
this.freeConnection(connection);
}
}
@Override
public List runSqlAsList(String sql, Object... argList) throws RuntimeDaoException{
return getManager().runSqlAsList(sql, argList);
}
@Override
public List