package org.nutz.dao.util; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.sql.DataSource; import org.nutz.dao.Chain; import org.nutz.dao.Condition; import org.nutz.dao.ConnCallback; import org.nutz.dao.Dao; import org.nutz.dao.DaoException; import org.nutz.dao.FieldFilter; import org.nutz.dao.Sqls; import org.nutz.dao.TableName; import org.nutz.dao.entity.Entity; import org.nutz.dao.entity.MappingField; import org.nutz.dao.entity.annotation.Table; import org.nutz.dao.impl.NutDao; import org.nutz.dao.jdbc.JdbcExpert; import org.nutz.dao.jdbc.Jdbcs; import org.nutz.dao.jdbc.ValueAdaptor; import org.nutz.dao.pager.Pager; import org.nutz.dao.sql.Sql; import org.nutz.dao.sql.SqlCallback; import org.nutz.lang.Lang; import org.nutz.lang.Strings; import org.nutz.log.Log; import org.nutz.log.Logs; import org.nutz.resource.Scans; import org.nutz.trans.Molecule; import org.nutz.trans.Trans; /** * Dao 的帮助函数 * * @author zozoh(zozohtnt@gmail.com) * @author wendal(wendal1985@gmail.com) * @author cqyunqin */ public abstract class Daos { private static final Log log = Logs.get(); public static void safeClose(Statement stat, ResultSet rs) { safeClose(rs); safeClose(stat); } public static void safeClose(Statement stat) { if (null != stat) try { stat.close(); } catch (Throwable e) {} } public static void safeClose(ResultSet rs) { if (null != rs) try { rs.close(); } catch (Throwable e) {} } public static int getColumnIndex(ResultSetMetaData meta, String colName) throws SQLException { if (meta == null) return 0; int columnCount = meta.getColumnCount(); for (int i = 1; i <= columnCount; i++) if (meta.getColumnName(i).equalsIgnoreCase(colName)) return i; // TODO 尝试一下meta.getColumnLabel? log.infof("Can not find @Column(%s) in table/view (%s)", colName, meta.getTableName(1)); throw Lang.makeThrow(SQLException.class, "Can not find @Column(%s)", colName); } public static boolean isIntLikeColumn(ResultSetMetaData meta, int index) throws SQLException { switch (meta.getColumnType(index)) { case Types.BIGINT: case Types.INTEGER: case Types.SMALLINT: case Types.TINYINT: case Types.NUMERIC: return true; } return false; } public static Pager updatePagerCount(Pager pager, Dao dao, Class<?> entityType, Condition cnd) { if (null != pager) { pager.setRecordCount(dao.count(entityType, cnd)); } return pager; } public static Pager updatePagerCount(Pager pager, Dao dao, String tableName, Condition cnd) { if (null != pager) { pager.setRecordCount(dao.count(tableName, cnd)); } return pager; } public static <T> List<T> queryList(Dao dao, Class<T> klass, String sql_str) { Sql sql = Sqls.create(sql_str) .setCallback(Sqls.callback.entities()) .setEntity(dao.getEntity(klass)); dao.execute(sql); return sql.getList(klass); } public static Object query(Dao dao, String sql_str, SqlCallback callback) { Sql sql = Sqls.create(sql_str).setCallback(callback); dao.execute(sql); return sql.getResult(); } public static <T> List<T> queryWithLinks( final Dao dao, final Class<T> classOfT, final Condition cnd, final Pager pager, final String regex) { Molecule<List<T>> molecule = new Molecule<List<T>>() { public void run() { List<T> list = dao.query(classOfT, cnd, pager); for (T t : list) dao.fetchLinks(t, regex); setObj(list); } }; return Trans.exec(molecule); } /*根据Pojo生成数据字典,zdoc格式*/ public static StringBuilder dataDict(DataSource ds, String...packages) { StringBuilder sb = new StringBuilder(); List<Class<?>> ks = new ArrayList<Class<?>>(); for (String packageName : packages) { ks.addAll(Scans.me().scanPackage(packageName)); } Iterator<Class<?>> it = ks.iterator(); while (it.hasNext()) { Class<?> klass = it.next(); if (klass.getAnnotation(Table.class) == null) it.remove(); } //log.infof("Found %d table class", ks.size()); JdbcExpert exp = Jdbcs.getExpert(ds); NutDao dao = new NutDao(ds); Method evalFieldType; try { evalFieldType = exp.getClass().getDeclaredMethod("evalFieldType", MappingField.class); } catch (Throwable e) { throw Lang.wrapThrow(e); } evalFieldType.setAccessible(true); Entity<?> entity = null; String line = "-------------------------------------------------------------------\n"; sb.append("#title:数据字典\n"); sb.append("#author:wendal\n"); sb.append("#index:0,1\n").append(line); for (Class<?> klass : ks) { sb.append(line); entity = dao.getEntity(klass); sb.append("表名 ").append(entity.getTableName()).append("\n\n"); if (!Strings.isBlank(entity.getTableComment())) sb.append("表注释: ").append(entity.getTableComment()); sb.append("\t").append("Java类名 ").append(klass.getName()).append("\n\n"); sb.append("\t||序号||列名||数据类型||主键||非空||默认值||java属性名||java类型||注释||\n"); int index = 1; for (MappingField field : entity.getMappingFields()) { String dataType; try { dataType = (String) evalFieldType.invoke(exp, field); } catch (Throwable e) { throw Lang.wrapThrow(e); //不可能发生的 } sb.append("\t||").append(index++).append("||") .append(field.getColumnName()).append("||") .append(dataType).append("||") .append(field.isPk()).append("||") .append(field.isNotNull()).append("||") .append(field.getDefaultValue(null) == null ? " " : field.getDefaultValue(null)).append("||") .append(field.getName()).append("||") .append(field.getTypeClass().getName()).append("||") .append(field.getColumnComment() == null ? " " : field.getColumnComment()).append("||\n"); } } return sb; } /** * 查询sql并把结果放入传入的class组成的List中 */ public static <T> List<T> query(Dao dao, Class<T> classOfT, String sql, Condition cnd, Pager pager) { Sql sql2 = Sqls.queryEntity(sql); sql2.setEntity(dao.getEntity(classOfT)); sql2.setCondition(cnd); sql2.setPager(pager); dao.execute(sql2); return sql2.getList(classOfT); } /** * 查询某sql的结果条数 */ public static int queryCount(Dao dao, String sql) { Sql sql2 = Sqls.fetchLong("select count(1) FROM ("+sql+")"); dao.execute(sql2); return sql2.getInt(); } /** * 执行一个特殊的Chain(事实上普通Chain也能执行,但不建议使用) * @see org.nutz.dao.Chain#addSpecial(String, Object) */ @SuppressWarnings({ "rawtypes" }) public static int updateBySpecialChain(Dao dao, Entity en, String tableName, Chain chain, Condition cnd) { if (en != null) tableName = en.getTableName(); if (tableName == null) throw Lang.makeThrow(DaoException.class, "tableName and en is NULL !!"); final StringBuilder sql = new StringBuilder("UPDATE ").append(tableName).append(" SET "); Chain head = chain.head(); final List<Object> values = new ArrayList<Object>(); final List<ValueAdaptor> adaptors = new ArrayList<ValueAdaptor>(); while (head != null) { MappingField mf = null; if (en != null) mf = en.getField(head.name()); String colName = head.name(); if (mf != null) colName = mf.getColumnName(); sql.append(colName).append("="); if (head.special()) { if (head.value() != null && head.value() instanceof String) { String str = (String)head.value(); if (str.length() > 0) { switch (str.charAt(0)) { case '+': case '-': case '*': case '/': case '%': case '&': case '^': case '|': sql.append(colName); break; } } } sql.append(head.value()); } else { sql.append("?"); values.add(head.value()); ValueAdaptor adaptor = Jdbcs.getAdaptorBy(head.value()); if (mf != null && mf.getAdaptor() != null) adaptor = mf.getAdaptor(); adaptors.add(adaptor); } sql.append(" "); head = head.next(); if (head != null) sql.append(", "); } if (cnd != null) sql.append(" ").append(cnd.toSql(en)); if (log.isDebugEnabled()) log.debug(sql); final int[] ints = new int[1]; dao.run(new ConnCallback() { public void invoke(Connection conn) throws Exception { PreparedStatement ps = conn.prepareStatement(sql.toString()); try { for (int i = 0; i < values.size(); i++) adaptors.get(i).set(ps, values.get(i), i + 1); ints[0] = ps.executeUpdate(); } finally { Daos.safeClose(ps); } } }); return ints[0]; } /** * 执行一个特殊的Chain(事实上普通Chain也能执行,但不建议使用) * @see org.nutz.dao.Chain#addSpecial(String, Object) */ @SuppressWarnings({ "rawtypes" }) public static void insertBySpecialChain(Dao dao, Entity en, String tableName, Chain chain) { if (en != null) tableName = en.getTableName(); if (tableName == null) throw Lang.makeThrow(DaoException.class, "tableName and en is NULL !!"); final StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append(" ("); StringBuilder _value_places = new StringBuilder(" VALUES("); final List<Object> values = new ArrayList<Object>(); final List<ValueAdaptor> adaptors = new ArrayList<ValueAdaptor>(); Chain head = chain.head(); while (head != null) { String colName = head.name(); MappingField mf = null; if (en != null) { mf = en.getField(colName); if (mf != null) colName = mf.getColumnName(); } sql.append(colName); if (head.special()) { _value_places.append(head.value()); } else { if (en != null) mf = en.getField(head.name()); _value_places.append("?"); values.add(head.value()); ValueAdaptor adaptor = Jdbcs.getAdaptorBy(head.value()); if (mf != null && mf.getAdaptor() != null) adaptor = mf.getAdaptor(); adaptors.add(adaptor); } head = head.next(); if (head != null) { sql.append(", "); _value_places.append(", "); } } sql.append(")"); _value_places.append(")"); sql.append(_value_places); if (log.isDebugEnabled()) log.debug(sql); dao.run(new ConnCallback() { public void invoke(Connection conn) throws Exception { PreparedStatement ps = conn.prepareStatement(sql.toString()); try { for (int i = 0; i < values.size(); i++) adaptors.get(i).set(ps, values.get(i), i + 1); ps.execute(); } finally { Daos.safeClose(ps); } } }); } public static void createTablesInPackage(Dao dao, String packageName, boolean force) { for (Class<?> klass : Scans.me().scanPackage(packageName)) { if (klass.getAnnotation(Table.class) != null) dao.create(klass, force); } } private static Class<?>[] iz = new Class<?>[]{Dao.class}; /** * 创建一个带FieldFilter的Dao代理实例. 注意,为避免出错,生成的Dao对象不应该传递到其他方法去. * @param dao 原始的Dao实例 * @param filter 字段过滤器 * @return 带FieldFilter的Dao代理实例 */ public static Dao ext(Dao dao, FieldFilter filter) { return ext(dao, filter, null); } /** * 创建一个带TableName的Dao代理实例. 注意,为避免出错,生成的Dao对象不应该传递到其他方法去. * @param dao 原始的Dao实例 * @param tableName 动态表名上下文 * @return 带TableName的Dao代理实例 */ public static Dao ext(Dao dao, Object tableName) { return ext(dao, null, tableName); } public static Dao ext(Dao dao, FieldFilter filter, Object tableName) { if (tableName == null && filter == null) return dao; ExtDaoInvocationHandler handler = new ExtDaoInvocationHandler(dao, filter, tableName); return (Dao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), iz, handler); } } class ExtDaoInvocationHandler implements InvocationHandler { protected ExtDaoInvocationHandler(Dao dao, FieldFilter filter, Object tableName) { this.dao = dao; this.filter = filter; this.tableName = tableName; } public Dao dao; public FieldFilter filter; public Object tableName; public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable { final Molecule<Object> m = new Molecule<Object>() { public void run() { try { setObj(method.invoke(dao, args)); } catch (IllegalArgumentException e) { throw Lang.wrapThrow(e); } catch (IllegalAccessException e) { throw Lang.wrapThrow(e); } catch (InvocationTargetException e) { throw Lang.wrapThrow(e.getTargetException()); } } }; if (filter != null && tableName != null) { TableName.run(tableName, new Runnable() { public void run() { filter.run(m); } }); return m.getObj(); } if (filter != null) filter.run(m); else TableName.run(tableName, m); return m.getObj(); } }