/* * JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jef.database.wrapper.processor; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Arrays; import java.util.List; import java.util.UUID; import jef.database.DbUtils; import jef.database.IQueryableEntity; import jef.database.OperateTarget; import jef.database.Sequence; import jef.database.dialect.DatabaseDialect; import jef.database.meta.DbProperty; import jef.database.meta.Feature; import jef.tools.Assert; import jef.tools.StringUtils; import jef.tools.reflect.Property; /** * 描述各种用于在插入后获取自增量字段值并更新到Bean中的回调方法 * * @author Administrator * */ public interface InsertStep { /** * 在插入前执行 * * @param data * @throws SQLException */ void callBefore(List<? extends IQueryableEntity> data) throws SQLException; /** * 在插入Batch后执行 * * @param data * @throws SQLException */ void callAfterBatch(List<? extends IQueryableEntity> data) throws SQLException; /** * 在插入后执行 * * @param data * @throws SQLException */ void callAfter(IQueryableEntity data) throws SQLException; /** * 自生成主键处理策略: 值已经预先生成,只需要要插入后调用此类更新到Bean中 * * 此接口 * * @author Administrator */ final static class SingleKeySetCallback implements InsertStep { private Property fieldName; private long key; private String sKey = null; // 必须传入Long型的Property public SingleKeySetCallback(Property fieldName, long key) { this.fieldName = fieldName; this.key = key; } // 必须传入Long型的Property public SingleKeySetCallback(Property fieldName, String key) { this.fieldName = fieldName; this.sKey = key; } public void callAfterBatch(List<? extends IQueryableEntity> data) throws SQLException { throw new UnsupportedOperationException(); } @Override public void callAfter(IQueryableEntity data) throws SQLException { if (sKey == null) { fieldName.set(data, key); } else { fieldName.set(data, sKey); } } public void callBefore(List<? extends IQueryableEntity> data) throws SQLException { } } /** * 对批量的Entity对象生成Sequence的主键,并赋值 * 这个回调方法要求在插入到数据库之前运行,直接更新Bean中的值。然后再用更新后的Bean插入数据库 * * @author Administrator */ final static class SequenceGenerateCallback implements InsertStep { private Property field; private Sequence holder; public SequenceGenerateCallback(Property fieldName, Sequence holder) { Assert.notNull(holder); this.field = fieldName; this.holder = holder; } public void callBefore(List<? extends IQueryableEntity> data) throws SQLException { for (IQueryableEntity o : data) { long key = -1; key = holder.next(); if (key > -1) { field.set(o, key); } else { throw new SQLException("AutoIncreatment generate error."); } } } public void callAfterBatch(List<? extends IQueryableEntity> data) throws SQLException { } @Override public void callAfter(IQueryableEntity data) throws SQLException { } } /** * 对批量的DO对象生成Sequence的主键,并赋值 * 这个回调方法要求在插入到数据库之前运行,直接更新Bean中的值。然后再用更新后的Bean插入数据库 * * @author Administrator */ static class GUIDGenerateCallback implements InsertStep { private Property field; private boolean removeDash; public GUIDGenerateCallback(Property fieldName, boolean b) { this.field = fieldName; this.removeDash = b; } public void callBefore(List<? extends IQueryableEntity> data) throws SQLException { for (IQueryableEntity o : data) { String key = UUID.randomUUID().toString(); if (removeDash) key = StringUtils.remove(key, '-'); field.set(o, key); } } @Override public void callAfterBatch(List<? extends IQueryableEntity> data) throws SQLException { } @Override public void callAfter(IQueryableEntity data) throws SQLException { } } /** * 用于在插入时返回Oracle Rowid的回调 * * @author jiyi * */ static class OracleRowidKeyCallback implements InsertStep, StatementPreparer { private Statement st; public void callAfterBatch(List<? extends IQueryableEntity> data) throws SQLException { throw new UnsupportedOperationException(); } @Override public void callAfter(IQueryableEntity entity) throws SQLException { ResultSet rs = st.getGeneratedKeys(); if (rs == null) throw new SQLException("getGeneratedKeys() returns null from the " + st + "."); try { Assert.isTrue(rs.next(), "The JDBC Driver may not support you operation."); entity.bindRowid(rs.getString(1)); } finally { rs.close(); } } public PreparedStatement doPrepareStatement(OperateTarget conn, String sql) throws SQLException { PreparedStatement pst = conn.prepareStatement(sql, 1); this.st = pst; return pst; } public void callBefore(List<? extends IQueryableEntity> data) throws SQLException { } } /** * 对批量或单个对象,赋予从JDBC得到的数据库所生成的主键值(需要驱动支持) * * @author Administrator * @threadsafe 由于缓存了此对象,因此要求必须是线程安全的 * */ static class JdbcAutoGeneratedKeyCallback implements InsertStep, StatementPreparer { private Property fieldName; // 主键字段 private String[] columnName; // 返回的行 private boolean isGuessMode; // 是否要猜测 private final ThreadLocal<Statement> st = new ThreadLocal<Statement>(); // Statement private boolean isBatchForFunction; private String getFunction; // /** * * @param fieldName * 必须是Long型号的Property,如果不是请先在外部包装 * @param guessMode * @param columnName */ public JdbcAutoGeneratedKeyCallback(Property fieldName, String columnName, DatabaseDialect profile) { this.fieldName = fieldName; this.isGuessMode = profile.has(Feature.BATCH_GENERATED_KEY_ONLY_LAST); this.isBatchForFunction = profile.has(Feature.BATCH_GENERATED_KEY_BY_FUNCTION); this.columnName = new String[] { columnName }; if (isBatchForFunction) { this.getFunction = profile.getProperty(DbProperty.GET_IDENTITY_FUNCTION); } } public void callAfterBatch(List<? extends IQueryableEntity> data) throws SQLException { if (data.size() == 0) return; if (isBatchForFunction) { byFunction(data); return; } ResultSet rs = st.get().getGeneratedKeys(); if (rs == null) throw new SQLException("getGeneratedKeys() returns null from the " + st + "."); try { if (isGuessMode) { Assert.isTrue(rs.next(), "The JDBC Driver may not support getGeneratedKeys() operation."); long max = rs.getLong(1); for (int i = data.size() - 1; i >= 0; i--) { IQueryableEntity o = data.get(i); fieldName.set(o, max--); } } else { for (IQueryableEntity o : data) { if (rs.next()) { fieldName.set(o, rs.getLong(1)); } else { throw new SQLException("The count of generated key from statement is not match to required."); } } if (rs.next()) { throw new SQLException("The count of generated key from statement is greater than the size."); } } } finally { rs.close(); } } private void byFunction(List<? extends IQueryableEntity> data) throws SQLException { long generatedKey = -1; Statement st2 = null; ResultSet rs = null; try { st2 = st.get().getConnection().createStatement(); rs = st2.executeQuery(this.getFunction); rs.next(); generatedKey = rs.getLong(1); } finally { DbUtils.close(st2); DbUtils.close(rs); } for (int i = data.size() - 1; i >= 0; i--) { IQueryableEntity o = data.get(i); fieldName.set(o, generatedKey--); } } public PreparedStatement doPrepareStatement(OperateTarget conn, String sql) throws SQLException { PreparedStatement pst = conn.prepareStatement(sql, columnName); this.st.set(pst); return pst; } public void callBefore(List<? extends IQueryableEntity> data) throws SQLException { } @Override public void callAfter(IQueryableEntity data) throws SQLException { callAfterBatch(Arrays.asList(data)); } } }