package org.nutz.dao.jdbc; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.math.BigDecimal; import java.sql.Blob; import java.sql.Clob; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; import java.util.Calendar; import javax.sql.DataSource; import org.nutz.castor.Castors; import org.nutz.dao.DaoException; import org.nutz.dao.entity.annotation.ColType; import org.nutz.dao.impl.entity.field.NutMappingField; import org.nutz.dao.impl.jdbc.BlobValueAdaptor; import org.nutz.dao.impl.jdbc.ClobValueAdaptor; import org.nutz.filepool.FilePool; import org.nutz.json.Json; import org.nutz.lang.Files; import org.nutz.lang.Lang; import org.nutz.lang.Mirror; import org.nutz.lang.Streams; import org.nutz.lang.Strings; import org.nutz.lang.meta.Email; import org.nutz.log.Log; import org.nutz.log.Logs; import org.nutz.trans.Trans; /** * 提供一些与 JDBC 有关的帮助函数 * * @author zozoh(zozohtnt@gmail.com) TODO 合并到NutConfig */ public abstract class Jdbcs { private static final Log log = Logs.get(); private static final JdbcExpertConfigFile conf; /* * 根据配置文件获取 experts 的列表 */ static { try { // 看看有没有用户自定义的映射文件 File f = Files.findFile("nutz_jdbc_experts.js");// TODO 不可配置?? // 如果没有则使用默认的映射文件 if (null == f) { conf = Json.fromJson(JdbcExpertConfigFile.class, new InputStreamReader(Jdbcs.class.getResourceAsStream("nutz_jdbc_experts.js"))).init(); } else conf = Json.fromJson(JdbcExpertConfigFile.class,Streams.fileInr("nutz_jdbc_experts.js")).init(); for (String key : conf.getExperts().keySet()) { // 检查一下正则表达式是否正确 // 在conf类中自行检查 // Pattern.compile(key,Pattern.DOTALL & // Pattern.CASE_INSENSITIVE); // 检查一下是否可以生成 Expert 的实例 conf.getExpert(key);// TODO 值得商讨 } } catch (Exception e) { throw Lang.wrapThrow(e); } if (log.isDebugEnabled()) log.debug("Jdbcs init complete"); } /** * 针对一个数据源,返回其专属的 JdbcExpert * * @param ds * 数据源 * @return 该数据库的特殊驱动封装类 * * @see org.nutz.dao.jdbc.Jdbcs#getExpert(String, String) */ public static JdbcExpert getExpert(DataSource ds) { log.info("Get Connection from DataSource for JdbcExpert"); Connection conn = null; try { conn = Trans.getConnectionAuto(ds); DatabaseMetaData meta = conn.getMetaData(); String pnm = meta.getDatabaseProductName(); String ver = meta.getDatabaseProductVersion(); return getExpert(pnm, ver); } catch (Throwable e) { throw Lang.wrapThrow(e); } finally { Trans.closeConnectionAuto(conn); } } /** * 根据数据库的产品名称,获取其专属的 Expert * <p> * 映射的规则存放在 JSON 文件 "nutz_jdbc_experts.js" 中,你可以通过建立这个文件修改 Nutz 的默认映射规则 * <p> * 比如下面的文件,将支持两种数据库 * * <pre> * { * experts : { * "postgresql.*" : "org.nutz.dao.impl.jdbc.psql.PostgresqlExpert", * "mysql.*" : "org.nutz.dao.impl.jdbc.mysql.MysqlExpert" * }, * config : { * "temp-home" : "~/.nutz/tmp/dao/", * "temp-max" : 2000 * } * } * </pre> * * 本函数传入的两个参数将会被: * * <pre> * String.format("%s::NUTZ_JDBC::%s", productName, version); * </pre> * * 并被你声明的正则表达式(expert 段下的键值)依次匹配,如果匹配上了,就会用相应的类当作驱动封装类 * * @param productName * 数据库产品名称 * @param version * 数据库版本号 * * @return 该数据库的特殊驱动封装类 * * @see java.sql.Connection#getMetaData() * @see java.sql.DatabaseMetaData#getDatabaseProductName() */ public static JdbcExpert getExpert(String productName, String version) { String dbName = String.format("%s::NUTZ_JDBC::%s", productName, version).toLowerCase(); JdbcExpert re = conf.matchExpert(dbName); if (null == re) { log.warnf("Can not support database '%s %s', fallback to MySql 5", productName, version); re = conf.matchExpert("mysql 5"); } return re; } public static ValueAdaptor getAdaptorBy(Object obj) { if (null == obj) return Adaptor.asNull; return getAdaptor(Mirror.me(obj)); } public static ValueAdaptor getAdaptor(Mirror<?> mirror) { // String and char if (mirror.isStringLike()) return Jdbcs.Adaptor.asString; // Int if (mirror.isInt()) return Jdbcs.Adaptor.asInteger; // Boolean if (mirror.isBoolean()) return Jdbcs.Adaptor.asBoolean; // Long if (mirror.isLong()) return Jdbcs.Adaptor.asLong; // Enum if (mirror.isEnum()) return Jdbcs.Adaptor.asEnumChar; // Char if (mirror.isChar()) return Jdbcs.Adaptor.asChar; // Timestamp if (mirror.isOf(Timestamp.class)) return Jdbcs.Adaptor.asTimestamp; // Byte if (mirror.isByte()) return Jdbcs.Adaptor.asByte; // Short if (mirror.isShort()) return Jdbcs.Adaptor.asShort; // Float if (mirror.isFloat()) return Jdbcs.Adaptor.asFloat; // Double if (mirror.isDouble()) return Jdbcs.Adaptor.asDouble; // BigDecimal if (mirror.isOf(BigDecimal.class)) return Jdbcs.Adaptor.asBigDecimal; // java.sql.Date if (mirror.isOf(java.sql.Date.class)) return Jdbcs.Adaptor.asSqlDate; // java.sql.Time if (mirror.isOf(java.sql.Time.class)) return Jdbcs.Adaptor.asSqlTime; // Calendar if (mirror.isOf(Calendar.class)) return Jdbcs.Adaptor.asCalendar; // java.util.Date if (mirror.isOf(java.util.Date.class)) return Jdbcs.Adaptor.asDate; // Blob if (mirror.isOf(Blob.class)) return new BlobValueAdaptor(conf.getPool()); // Clob if (mirror.isOf(Clob.class)) return new ClobValueAdaptor(conf.getPool()); // byte[] if (mirror.getType().isArray() && mirror.getType().getComponentType() == byte.class) { return Jdbcs.Adaptor.asBytes; } // inputstream if (mirror.isOf(InputStream.class)) return Jdbcs.Adaptor.asBinaryStream; if (mirror.isOf(Reader.class)) return Jdbcs.Adaptor.asReader; // 默认情况 return Jdbcs.Adaptor.asString; } public static class Adaptor { /** * 空值适配器 */ public static final ValueAdaptor asNull = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { return null; } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { stat.setNull(i, Types.NULL); }; }; /** * 字符串适配器 */ public static final ValueAdaptor asString = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { return rs.getString(colName); } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setString(i, null); } else { stat.setString(i, Castors.me().castToString(obj)); } } }; /** * 字符适配器 */ public static final ValueAdaptor asChar = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { String re = Strings.trim(rs.getString(colName)); if (re == null || re.length() == 0) return null; return re; } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setString(i, null); } else { String s; if (obj instanceof Character) { int c = ((Character) obj).charValue(); if (c >= 0 && c <= 32) s = " "; else s = String.valueOf((char) c); } else s = obj.toString(); stat.setString(i, s); } } }; /** * 整型适配器 */ public static final ValueAdaptor asInteger = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { int re = rs.getInt(colName); return rs.wasNull() ? null : re; } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setNull(i, Types.INTEGER); } else { int v; if (obj instanceof Number) v = ((Number) obj).intValue(); else v = Castors.me().castTo(obj.toString(), int.class); stat.setInt(i, v); } } }; /** * 大数适配器 */ public static final ValueAdaptor asBigDecimal = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { return rs.getBigDecimal(colName); } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setNull(i, Types.BIGINT); } else { BigDecimal v; if (obj instanceof BigDecimal) v = (BigDecimal) obj; else if (obj instanceof Number) v = BigDecimal.valueOf(((Number) obj).longValue()); else v = new BigDecimal(obj.toString()); stat.setBigDecimal(i, v); } } }; /** * 布尔适配器 * <p> * 对 Oracle,Types.BOOLEAN 对于 setNull 是不工作的 因此 OracleExpert 会用一个新的 * Adaptor 处理自己这种特殊情况 */ public static final ValueAdaptor asBoolean = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { boolean re = rs.getBoolean(colName); return rs.wasNull() ? null : re; } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setNull(i, Types.BOOLEAN); } else { boolean v; if (obj instanceof Boolean) v = (Boolean) obj; else if (obj instanceof Number) v = ((Number) obj).intValue() > 0; else if (obj instanceof Character) v = Character.toUpperCase((Character) obj) == 'T'; else v = Boolean.valueOf(obj.toString()); stat.setBoolean(i, v); } } }; /** * 长整适配器 */ public static final ValueAdaptor asLong = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { long re = rs.getLong(colName); return rs.wasNull() ? null : re; } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setNull(i, Types.INTEGER); } else { long v; if (obj instanceof Number) v = ((Number) obj).longValue(); else v = Castors.me().castTo(obj.toString(), long.class); stat.setLong(i, v); } } }; /** * 字节适配器 */ public static final ValueAdaptor asByte = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { byte re = rs.getByte(colName); return rs.wasNull() ? null : re; } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setNull(i, Types.TINYINT); } else { byte v; if (obj instanceof Number) v = ((Number) obj).byteValue(); else v = Castors.me().castTo(obj.toString(), byte.class); stat.setByte(i, v); } } }; /** * 短整型适配器 */ public static final ValueAdaptor asShort = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { short re = rs.getShort(colName); return rs.wasNull() ? null : re; } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setNull(i, Types.SMALLINT); } else { short v; if (obj instanceof Number) v = ((Number) obj).shortValue(); else v = Castors.me().castTo(obj.toString(), short.class); stat.setShort(i, v); } } }; /** * 浮点适配器 */ public static final ValueAdaptor asFloat = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { float re = rs.getFloat(colName); return rs.wasNull() ? null : re; } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setNull(i, Types.FLOAT); } else { float v; if (obj instanceof Number) v = ((Number) obj).floatValue(); else v = Castors.me().castTo(obj.toString(), float.class); stat.setFloat(i, v); } } }; /** * 双精度浮点适配器 */ public static final ValueAdaptor asDouble = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { double re = rs.getDouble(colName); return rs.wasNull() ? null : re; } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setNull(i, Types.DOUBLE); } else { double v; if (obj instanceof Number) v = ((Number) obj).doubleValue(); else v = Castors.me().castTo(obj.toString(), double.class); stat.setDouble(i, v); } } }; /** * 日历适配器 */ public static final ValueAdaptor asCalendar = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { Timestamp ts = rs.getTimestamp(colName); if (null == ts) return null; Calendar c = Calendar.getInstance(); c.setTimeInMillis(ts.getTime()); return c; } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setNull(i, Types.TIMESTAMP); } else { Timestamp v; if (obj instanceof Calendar) v = new Timestamp(((Calendar) obj).getTimeInMillis()); else v = Castors.me().castTo(obj, Timestamp.class); stat.setTimestamp(i, v); } } }; /** * 时间戳适配器 */ public static final ValueAdaptor asTimestamp = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { return rs.getTimestamp(colName); } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setNull(i, Types.TIMESTAMP); } else { Timestamp v; if (obj instanceof Timestamp) v = (Timestamp) obj; else v = Castors.me().castTo(obj, Timestamp.class); stat.setTimestamp(i, v); } } }; /** * 日期适配器 */ public static final ValueAdaptor asDate = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { Timestamp ts = rs.getTimestamp(colName); return null == ts ? null : new java.util.Date(ts.getTime()); } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { Timestamp v; if (null == obj) { stat.setNull(i, Types.TIMESTAMP); } else { if (obj instanceof java.util.Date) v = new Timestamp(((java.util.Date) obj).getTime()); else v = Castors.me().castTo(obj, Timestamp.class); stat.setTimestamp(i, v); } } }; /** * Sql 日期适配器 */ public static final ValueAdaptor asSqlDate = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { return rs.getDate(colName); } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setNull(i, Types.DATE); } else { java.sql.Date v; if (obj instanceof java.sql.Date) v = (java.sql.Date) obj; else v = Castors.me().castTo(obj, java.sql.Date.class); stat.setDate(i, v); } } }; /** * Sql 时间适配器 */ public static final ValueAdaptor asSqlTime = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { return rs.getTime(colName); } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { java.sql.Time v; if (null == obj) { stat.setNull(i, Types.TIME); } else { if (obj instanceof java.sql.Time) v = (java.sql.Time) obj; else v = Castors.me().castTo(obj, java.sql.Time.class); stat.setTime(i, v); } } }; /** * 数字枚举适配器 */ public static final ValueAdaptor asEnumInt = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { int re = rs.getInt(colName); return rs.wasNull() ? null : re; } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setNull(i, Types.INTEGER); } else { int v; if (obj instanceof Enum<?>) v = ((Enum<?>) obj).ordinal(); else v = Castors.me().castTo(obj, int.class); stat.setInt(i, v); } } }; /** * 字符枚举适配器 */ public static final ValueAdaptor asEnumChar = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { return rs.getString(colName); } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { if (null == obj) { stat.setString(i, null); } else { String v = obj.toString(); stat.setString(i, v); } } }; /** * 默认对象适配器 */ public static final ValueAdaptor asObject = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { return rs.getObject(colName); } public void set(PreparedStatement stat, Object obj, int i) throws SQLException { stat.setObject(i, obj); } }; /** * 字节数组适配器 */ public static final ValueAdaptor asBytes = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { return rs.getBytes(colName); } public void set(PreparedStatement stat, Object obj, int index) throws SQLException { if (null == obj) { stat.setNull(index, Types.BINARY); } else { stat.setBytes(index, (byte[]) obj); } } }; public static final ValueAdaptor asBinaryStream = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { InputStream in = rs.getBinaryStream(colName); if (in == null) { return in; } try { File f = File.createTempFile("nutzdao_blob", ".tmp"); Files.write(f, in); in.close(); return new ReadOnceInputStream(f); } catch (IOException e) { throw Lang.wrapThrow(e); } } public void set(PreparedStatement stat, Object obj, int index) throws SQLException { if (null == obj) { stat.setNull(index, Types.BINARY); } else { if (obj instanceof ByteArrayInputStream) { stat.setBinaryStream(index, (InputStream)obj, ((ByteArrayInputStream)obj).available()); } else if (obj instanceof InputStream) { if (obj instanceof ReadOnceInputStream) { if (((ReadOnceInputStream)obj).readed) { throw new DaoException(""); } } try { File f = Jdbcs.getFilePool().createFile(".dat"); Streams.writeAndClose(new FileOutputStream(f), (InputStream)obj); stat.setBinaryStream(index, new FileInputStream(f), f.length()); } catch (FileNotFoundException e) { System.gc(); try { File f = Jdbcs.getFilePool().createFile(".dat"); Streams.writeAndClose(new FileOutputStream(f), (InputStream)obj); stat.setBinaryStream(index, new FileInputStream(f), f.length()); } catch (FileNotFoundException e2) { throw Lang.impossible(); } } } } } }; public static final ValueAdaptor asReader = new ValueAdaptor() { public Object get(ResultSet rs, String colName) throws SQLException { return rs.getCharacterStream(colName); } public void set(PreparedStatement stat, Object obj, int index) throws SQLException { if (null == obj) { stat.setNull(index, Types.BINARY); } else { setCharacterStream(index, obj, stat); } } }; } /** * 根据字段现有的信息,尽可能猜测一下字段的数据库类型 * * @param ef * 映射字段 */ public static void guessEntityFieldColumnType(NutMappingField ef) { Mirror<?> mirror = ef.getTypeMirror(); // 整型 if (mirror.isInt()) { ef.setColumnType(ColType.INT); ef.setWidth(8); } // 字符串 else if (mirror.isStringLike() || mirror.is(Email.class)) { ef.setColumnType(ColType.VARCHAR); ef.setWidth(50); } // 长整型 else if (mirror.isLong()) { ef.setColumnType(ColType.INT); ef.setWidth(16); } // 枚举 else if (mirror.isEnum()) { ef.setColumnType(ColType.VARCHAR); ef.setWidth(20); } // 时间戳 else if (mirror.is(Timestamp.class)) { ef.setColumnType(ColType.TIMESTAMP); } // 布尔 else if (mirror.isBoolean()) { ef.setColumnType(ColType.BOOLEAN); ef.setWidth(1); } // 字符 else if (mirror.isChar()) { ef.setColumnType(ColType.CHAR); ef.setWidth(4); } // 日期 else if (mirror.is(java.sql.Date.class)) { ef.setColumnType(ColType.DATE); } // 时间 else if (mirror.is(java.sql.Time.class)) { ef.setColumnType(ColType.TIME); } // 日期时间 else if (mirror.isOf(Calendar.class) || mirror.is(java.util.Date.class)) { ef.setColumnType(ColType.DATETIME); } // 大数 else if (mirror.is(BigDecimal.class)) { ef.setColumnType(ColType.INT); ef.setWidth(32); } // 短整型 else if (mirror.isShort()) { ef.setColumnType(ColType.INT); ef.setWidth(4); } // 字节 else if (mirror.isByte()) { ef.setColumnType(ColType.INT); ef.setWidth(2); } // 浮点 else if (mirror.isFloat()) { ef.setColumnType(ColType.FLOAT); } // 双精度浮点 else if (mirror.isDouble()) { ef.setColumnType(ColType.FLOAT); } // 文本流 else if (mirror.isOf(Reader.class) || mirror.isOf(Clob.class)) { ef.setColumnType(ColType.TEXT); } // 二进制流 else if (mirror.isOf(InputStream.class) || mirror.is(byte[].class) || mirror.isOf(Blob.class)) { ef.setColumnType(ColType.BINARY); } /* * 上面的都不是? 那就当作字符串好了,反正可以 toString */ else { if (log.isDebugEnabled()) log.debugf("take field '%s(%s)'(%s) as VARCHAR(50)", ef.getName(), Lang.getTypeClass(ef.getType()).getName(), ef.getEntity().getType().getName()); ef.setColumnType(ColType.VARCHAR); ef.setWidth(50); } } public static FilePool getFilePool() { return conf.getPool(); } public static void setCharacterStream(int index, Object obj, PreparedStatement stat) throws SQLException { try { File f = Jdbcs.getFilePool().createFile(".dat"); Streams.writeAndClose(new FileWriter(f), (Reader)obj); stat.setCharacterStream(index, new FileReader(f), f.length()); } catch (FileNotFoundException e) { throw Lang.impossible(); } catch (IOException e) { throw Lang.wrapThrow(e); } } } class ReadOnceInputStream extends FilterInputStream { private File f; public boolean readed; protected ReadOnceInputStream(File f) throws FileNotFoundException { super(new FileInputStream(f)); this.f = f; } public int read() throws IOException { readed = true; return super.read(); } public int read(byte[] b) throws IOException { readed = true; return super.read(b); } public int read(byte[] b, int off, int len) throws IOException { readed = true; return super.read(b, off, len); } public void close() throws IOException { super.close(); f.delete(); } protected void finalize() throws Throwable { f.delete(); super.finalize(); } }