前言 为了将最近学习的JDBC、反射、设计模式综合起来,就学习手写了ORM框架,ORM全称Object Relationship Mapping,意思是对象关系映射,主要思想有三点:1、数据库中的表结构和类结构对应;2、表中字段和类属性对应;3、表中记录和类的对象对应。这就是实现对象关系映射的基本要求,对应到框架中就会有很多接口和实现类。
具体实现 核心架构 框架src目录下一共有四个package,分别是:bean包,负责封装数据库表中的信息以及生成与表结构对应的类;core包,负责与数据库的连接操作以及数据类型的转换; pool包,连接池功能,加入连接池,使查询的效率大幅提高;utils包,这个包下的类都是常用的工具类,包括反射、JDBC查询操作、字符串操作等。src目录下还有一个db.properties文件,这个文件中保存着包括驱动类、数据库连接url、数据库用户名密码等信息,后面创建工程使用本框架时,直接在该文件中修改相关信息即可。下面我们讲一讲框架内部具体的实现。
bean包 ColumnInfo类 ColumnInfo类的主要作用是封装了表中一个字段的信息,包括字段名称、字段的数据类型、字段的键类型。后面我们要获取表中一个字段的名称、类型、键类型都通过这个类的对象的ge方法获得。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public class ColumnInfo { private String name; private String dataType; private int keyType; public String getName () { return name; } public void setName (String name) { this .name = name; } public String getDataType () { return dataType; } public void setDataType (String dataType) { this .dataType = dataType; } public int getKeyType () { return keyType; } public void setKeyType (int keyType) { this .keyType = keyType; } public ColumnInfo (String name, String dataType, int keyType) { super (); this .name = name; this .dataType = dataType; this .keyType = keyType; } public ColumnInfo () { } }
Configuration类 Configuration类用于管理配置信息,它将db.properties文件中的信息全部封转成私有属性,为后续的数据库连接做准备。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 public class Configuration { private String driver ; private String url ; private String user ; private String pwd ; private String usingDB ; private String srcPath ; private String poPackage ; private String queryClass; private int poolMinSize; private int poolMaxSize; public int getPoolMinSize () { return poolMinSize; } public void setPoolMinSize (int poolMinSize) { this .poolMinSize = poolMinSize; } public int getPoolMaxSize () { return poolMaxSize; } public void setPoolMaxSize (int poolMaxSize) { this .poolMaxSize = poolMaxSize; } public Configuration () { } public Configuration (String driver, String url, String user, String pwd, String usingDB, String srcPath, String poPackage) { super (); this .driver = driver; this .url = url; this .user = user; this .pwd = pwd; this .usingDB = usingDB; this .srcPath = srcPath; this .poPackage = poPackage; } public String getQueryClass () { return queryClass; } public void setQueryClass (String queryClass) { this .queryClass = queryClass; } public String getDriver () { return driver; } public void setDriver (String driver) { this .driver = driver; } public String getUrl () { return url; } public void setUrl (String url) { this .url = url; } public String getUser () { return user; } public void setUser (String user) { this .user = user; } public String getPwd () { return pwd; } public void setPwd (String pwd) { this .pwd = pwd; } public String getUsingDB () { return usingDB; } public void setUsingDB (String usingDB) { this .usingDB = usingDB; } public String getSrcPath () { return srcPath; } public void setSrcPath (String srcPath) { this .srcPath = srcPath; } public String getPoPackage () { return poPackage; } public void setPoPackage (String poPackage) { this .poPackage = poPackage; } }
JavaFieldGetSet类 JavaFieldGetSet类封装了java属性和get、set方法的源代码,如属性的源码信息、get方法的源码信息、set方法的源码信息,都以字符串类型保存,为后面生成与数据库表对应的Java类做准备。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public class JavaFieldGetSet { private String fieldInfo; private String getInfo; private String setInfo; @Override public String toString () { System.out.println(fieldInfo); System.out.println(getInfo); System.out.println(setInfo); return super .toString(); } public String getFieldInfo () { return fieldInfo; } public void setFieldInfo (String fieldInfo) { this .fieldInfo = fieldInfo; } public String getGetInfo () { return getInfo; } public void setGetInfo (String getInfo) { this .getInfo = getInfo; } public String getSetInfo () { return setInfo; } public void setSetInfo (String setInfo) { this .setInfo = setInfo; } public JavaFieldGetSet (String fieldInfo, String getInfo, String setInfo) { super (); this .fieldInfo = fieldInfo; this .getInfo = getInfo; this .setInfo = setInfo; } public JavaFieldGetSet () { } }
TableInfo类 TableInfo类用来存储表结构的信息,包括表名、所有字段的信息、唯一的主键等,通过TableInfo对象能够获得该表的主键等信息,为后续执行SQL语句对数据库进行操作服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 public class TableInfo { private String tname; private Map<String, ColumnInfo> columns; private ColumnInfo onlyPriKey; private List<ColumnInfo> priKeys; public List<ColumnInfo> getPriKeys () { return priKeys; } public void setPriKeys (List<ColumnInfo> priKeys) { this .priKeys = priKeys; } public String getTname () { return tname; } public void setTname (String tname) { this .tname = tname; } public Map<String, ColumnInfo> getColumns () { return columns; } public void setColumns (Map<String, ColumnInfo> columns) { this .columns = columns; } public ColumnInfo getOnlyPriKey () { return onlyPriKey; } public void setOnlyPriKey (ColumnInfo onlyPriKey) { this .onlyPriKey = onlyPriKey; } public TableInfo (String tname, Map<String, ColumnInfo> columns, ColumnInfo onlyPriKey) { super (); this .tname = tname; this .columns = columns; this .onlyPriKey = onlyPriKey; } public TableInfo () { } public TableInfo (String tname, List<ColumnInfo> priKeys, Map<String, ColumnInfo> columns) { super (); this .tname = tname; this .columns = columns; this .priKeys = priKeys; } }
core包 CallBack接口 CallBack接口是回调接口, 提供doExecut方法的定义,方便后面Query类不同的查询需求重新实现doExecute方法。
1 2 3 4 5 6 7 8 public interface CallBack { public Object doExecute (Connection conn,PreparedStatement ps,ResultSet rs) ; }
DBManager类 DBManager类根据配置信息,维持连接对象的管理。通过静态代码块,在加载该类的同时加载且只加载一次配置信息,并且提供获得和创建Connetcion对象的两个方法,以及关闭ResultSet、Statement、Connection的close方法。在创建了连接池对象pool后,大大提高了效率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 public class DBManager { private static Configuration conf; private static DBConnPool pool; static { Properties pros = new Properties(); try { pros.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties" )); } catch (IOException e) { e.printStackTrace(); } conf = new Configuration(); conf.setDriver(pros.getProperty("driver" )); conf.setPoPackage(pros.getProperty("poPackage" )); conf.setPwd(pros.getProperty("pwd" )); conf.setSrcPath(pros.getProperty("srcPath" )); conf.setUrl(pros.getProperty("url" )); conf.setUser(pros.getProperty("user" )); conf.setUsingDB(pros.getProperty("usingDB" )); conf.setQueryClass(pros.getProperty("queryClass" )); conf.setPoolMaxSize(Integer.parseInt(pros.getProperty("poolMaxSize" ))); conf.setPoolMinSize(Integer.parseInt(pros.getProperty("poolMinSize" ))); System.out.println(TableContext.class); } public static Connection createConn () { try { Class.forName(conf.getDriver()); return DriverManager.getConnection(conf.getUrl(), conf.getUser(),conf.getPwd()); } catch (Exception e) { e.printStackTrace(); return null ; } } public static Connection getConn () { if (pool==null ) { pool = new DBConnPool(); } return pool.getConnection(); } public static void close (ResultSet rs,Statement ps,Connection conn) { if (rs!=null ) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps!=null ) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } pool.close(conn); } public static void close (Statement ps,Connection conn) { if (ps!=null ) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } pool.close(conn); } public static void close (Connection conn) { pool.close(conn); } public static Configuration getConf () { return conf; } }
MySqlQuery类 MySqlQuery类负责针对Mysql数据库的查询,继承于Query类,由于后来涉及到数据库可能以后会更换,就将查询操作全部归入到了抽象类Query类中去,因为查询的操作大同小异,对这个类不再做赘述。
1 2 3 4 5 6 7 8 9 10 11 12 @SuppressWarnings ("all" )public class MySqlQuery extends Query { @Override public Object queryPagenate (int pageNum, int size) { return null ; } }
MySqlTypeConvertor类 MySqlTypeConvertor类实现了TypeConvertor接口,该类主要用于将MySql数据库中的数据类型转为Java数据类型,因为我们针对的是对数据库的操作,这里只提供从数据库到Java的类型转换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class MySqlTypeConvertor implements TypeConvertor { @Override public String databaseType2JavaType (String columnType) { if ("varchar" .equalsIgnoreCase(columnType)||"char" .equalsIgnoreCase(columnType)) { return "String" ; }else if ("int" .equalsIgnoreCase(columnType) ||"tinyint" .equalsIgnoreCase(columnType) ||"smallint" .equalsIgnoreCase(columnType) ||"integer" .equalsIgnoreCase(columnType) ) { return "Integer" ; }else if ("bigint" .equalsIgnoreCase(columnType)) { return "Long" ; }else if ("double" .equalsIgnoreCase(columnType)||"float" .equalsIgnoreCase(columnType)) { return "Double" ; }else if ("clob" .equalsIgnoreCase(columnType)) { return "java.sql.Clob" ; }else if ("blob" .equalsIgnoreCase(columnType)) { return "java.sql.Blob" ; }else if ("date" .equalsIgnoreCase(columnType)) { return "java.sql.Date" ; }else if ("time" .equalsIgnoreCase(columnType)) { return "java.sql.Time" ; }else if ("timestamp" .equalsIgnoreCase(columnType)) { return "java.sql.Timestamp" ; } return null ; } @Override public String javaType2DatabaseType (String javaDataType) { return null ; } }
Query抽象类 Query抽象类是负责查询的,对外提供服务的核心类,提供executeQueryTemplate方法,是JDBC操作的模板方法;提供executeDML执行DML语句方法;提供insert、delete、update的增加、删除、更改方法,还提供了五种查询方法,具体实现见代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 @SuppressWarnings ("all" )public abstract class Query implements Cloneable { public Object executeQueryTemplate (String sql,Object[] params,Class clazz,CallBack back) { Connection conn = DBManager.getConn(); PreparedStatement ps = null ; ResultSet rs = null ; try { ps=conn.prepareStatement(sql); JDBCUtils.handleParams(ps, params); System.out.println(ps); rs = ps.executeQuery(); return back.doExecute(conn, ps, rs); } catch (Exception e) { e.printStackTrace(); return null ; }finally { DBManager.close(ps, conn); } } public int executeDML (String sql,Object[] params) { Connection conn = DBManager.getConn(); int count = 0 ; PreparedStatement ps = null ; try { ps=conn.prepareStatement(sql); JDBCUtils.handleParams(ps, params); System.out.println(ps); count = ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally { DBManager.close(ps, conn); } return count; } public void insert (Object obj) { Class c = obj.getClass(); List<Object> params = new ArrayList<Object>(); TableInfo tableInfo = TableContext.poClassTableMap.get(c); StringBuilder sql = new StringBuilder("insert into " +tableInfo.getTname()+" (" ); int countNotNullField = 0 ; Field[] fs = c.getDeclaredFields(); for (Field f:fs) { String fieldName = f.getName(); Object fieldValue = ReflectUtils.invokeGet(fieldName, obj); if (fieldValue!=null ) { countNotNullField++; sql.append(fieldName+"," ); params.add(fieldValue); } } sql.setCharAt(sql.length()-1 , ')' ); sql.append(" values (" ); for (int i=0 ;i<countNotNullField;i++) { sql.append("?," ); } sql.setCharAt(sql.length()-1 , ')' ); executeDML(sql.toString(), params.toArray()); } public void delete (Class clazz,Object id) { TableInfo tableInfo = TableContext.poClassTableMap.get(clazz); ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey(); String sql = "delete from " +tableInfo.getTname()+" where " +onlyPriKey.getName()+"=? " ; executeDML(sql, new Object[] {id}); } public void delete (Object obj) { Class c = obj.getClass(); TableInfo tableInfo = TableContext.poClassTableMap.get(c); ColumnInfo onlyPrikey = tableInfo.getOnlyPriKey(); Object priKeyValue = ReflectUtils.invokeGet(onlyPrikey.getName(), obj); delete(c,priKeyValue); } public int update (Object obj,String[] fieldNames) { Class c = obj.getClass(); List<Object> params = new ArrayList<Object>(); TableInfo tableInfo = TableContext.poClassTableMap.get(c); ColumnInfo priKey = tableInfo.getOnlyPriKey(); StringBuilder sql = new StringBuilder("update " +tableInfo.getTname()+" set " ); for (String fname:fieldNames) { Object fvalue = ReflectUtils.invokeGet(fname, obj); params.add(fvalue); sql.append(fname+"=?," ); } sql.setCharAt(sql.length()-1 , ' ' ); sql.append(" where " ); sql.append(priKey.getName()+"=?" ); params.add(ReflectUtils.invokeGet(priKey.getName(), obj)); return executeDML(sql.toString(), params.toArray()); } public List queryRows (final String sql,final Class clazz,final Object[] params) { return (List)executeQueryTemplate(sql, params, clazz, new CallBack() { @Override public Object doExecute (Connection conn, PreparedStatement ps, ResultSet rs) { List list = null ; try { ResultSetMetaData metaData = rs.getMetaData(); while (rs.next()) { if (list==null ) { list = new ArrayList(); } Object rowObj = clazz.newInstance(); for (int i =0 ;i<metaData.getColumnCount();i++) { String columnName = metaData.getColumnLabel(i+1 ); Object columnValue = rs.getObject(i+1 ); ReflectUtils.invokeSet(rowObj, columnName, columnValue); } list.add(rowObj); } } catch (Exception e) { e.printStackTrace(); } return list; } }); } public Object queryUniqueRow (String sql,Class clazz,Object[] params) { List list = queryRows(sql, clazz, params); return (list!=null &&list.size()>0 )?list.get(0 ):null ; } public Object queryById (Class clazz,Object id) { TableInfo tableInfo = TableContext.poClassTableMap.get(clazz); ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey(); String sql="select * from " +tableInfo.getTname()+" where " +onlyPriKey.getName()+"=?" ; return queryUniqueRow(sql, clazz, new Object[] {id}); } public Object queryValue (String sql,Object[] params) { return executeQueryTemplate(sql, params, null , new CallBack() { @Override public Object doExecute (Connection conn, PreparedStatement ps, ResultSet rs) { Object value = null ; try { while (rs.next()) { value = rs.getObject(1 ); } } catch (SQLException e) { e.printStackTrace(); } return value; } }); } public Number queryNumber (String sql,Object[] params) { return (Number)queryValue(sql, params); } public abstract Object queryPagenate (int pageNum,int size) ; @Override protected Object clone () throws CloneNotSupportedException { return super .clone(); } }
QueryFactory类 QueryFactory类是创建Query对象的工厂类,用到了克隆模式,单例模式,工厂模式,我对设计模式这块掌握的不是很清楚,后面会着重去研究一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class QueryFactory { private static Query prototypeObj; static { try { Class c = Class.forName(DBManager.getConf().getQueryClass()); prototypeObj = (Query)c.newInstance(); } catch (Exception e) { e.printStackTrace(); } } private QueryFactory () { } public static Query createQuery () { try { return (Query)prototypeObj.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null ; } } }
TableContext类 TableContext类负责获取管理数据库所有表结构和类结构的关系,并可以表结构生成类结构。静态代码块用于获取数据库对应的表的信息,updateJavaPOFile方法根据表结构生成对应的类结构,loadPOTables方法用于加载po包下面的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 public class TableContext { public static Map<String, TableInfo> tables = new HashMap<String, TableInfo>(); public static Map<Class, TableInfo> poClassTableMap = new HashMap<Class,TableInfo>(); public TableContext () { } static { try { Connection con = DBManager.getConn(); DatabaseMetaData dbmd = con.getMetaData(); ResultSet tableRet = dbmd.getTables(null , "%" , "%" , new String[]{"TABLE" }); while (tableRet.next()) { String tableName = (String)tableRet.getObject("TABLE_NAME" ); TableInfo ti = new TableInfo(tableName,new ArrayList<ColumnInfo>(),new HashMap<String,ColumnInfo>()); tables.put(tableName, ti); ResultSet set = dbmd.getColumns(null , "%" , tableName, "%" ); while (set.next()) { ColumnInfo ci = new ColumnInfo(set.getString("COLUMN_NAME" ),set.getString("TYPE_NAME" ),0 ); ti.getColumns().put(set.getString("COLUMN_NAME" ), ci); } ResultSet set2 = dbmd.getPrimaryKeys(null , "%" ,tableName); while (set2.next()) { ColumnInfo ci2 = (ColumnInfo)ti.getColumns().get(set2.getObject("COLUMN_NAME" )); ci2.setKeyType(1 ); ti.getPriKeys().add(ci2); } if (ti.getPriKeys().size()>0 ) { ti.setOnlyPriKey(ti.getPriKeys().get(0 )); } } } catch (SQLException e) { e.printStackTrace(); } updateJavaPOFile(); loadPOTables(); } public static void updateJavaPOFile () { Map<String,TableInfo> map = TableContext.tables; for (TableInfo t:map.values()) { JavaFileUtils.createJavaPOFile(t, new MySqlTypeConvertor()); } } public static void loadPOTables () { for (TableInfo tableInfo:tables.values()) { try { Class c = Class.forName(DBManager.getConf().getPoPackage()+"." +StringUtils.firstChar2UpperCase(tableInfo.getTname())); poClassTableMap.put(c, tableInfo); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } public static void main (String[] args) { Map<String, TableInfo> tables = TableContext.tables; System.out.println(tables); } }
TypeConvertor接口 TypeConvertor接口负责java数据类型和数据库数据类型的转换,对外提供将数据库数据类型转换成Java的数据类型的databaseType2JavaType方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public interface TypeConvertor { public String databaseType2JavaType (String columnType) ; public String javaType2DatabaseType (String javaDataType) ; }
pool包 DBConnPool类 DBConnPool类是连接池类,连接池的主要逻辑是我们创建一个连接池对象,并且设置连接池中初始连接的个数,当有需要连接时就从连接池中取出,用完再放回连接池中,而不是直接关闭连接,这样能大大提高处理效率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public class DBConnPool { private List<Connection> pool; private static final int POOL_MAX_SIZE = DBManager.getConf().getPoolMaxSize(); private static final int POOL_MIN_SIZE = DBManager.getConf().getPoolMinSize(); public void initPool () { if (pool==null ) { pool = new ArrayList<Connection>(); } while (pool.size()<DBConnPool.POOL_MIN_SIZE) { pool.add(DBManager.createConn()); System.out.println("初始化池,池中连接数:" +pool.size()); } } public synchronized Connection getConnection () { int last_index = pool.size()-1 ; Connection conn = pool.get(last_index); pool.remove(last_index); return conn; } public synchronized void close (Connection conn) { if (pool.size()>POOL_MAX_SIZE) { try { if (conn!=null ) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } }else { pool.add(conn); } } public DBConnPool () { initPool(); } }
utils包 JavaFileUtils类 JavaFileUtils类封装了生成Java文件常用的操作,提供createFieldGetSetSRC方法根据字段信息生成Java属性信息,createJavaSrc方法根据表信息生成Java类的源代码,createJavaPOFile方法生成po包下的Java类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 public class JavaFileUtils { public static JavaFieldGetSet createFieldGetSetSRC (ColumnInfo column,TypeConvertor convertor) { JavaFieldGetSet jfjs = new JavaFieldGetSet(); String javaFieldType = convertor.databaseType2JavaType(column.getDataType()); jfjs.setFieldInfo("\tprivate " +javaFieldType+" " +column.getName()+";\n" ); StringBuilder getSrc = new StringBuilder(); getSrc.append("\tpublic " +javaFieldType+" get" +StringUtils.firstChar2UpperCase(column.getName())+"(){\n" ); getSrc.append("\t\treturn " +column.getName()+";\n" ); getSrc.append("\t}\n" ); jfjs.setGetInfo(getSrc.toString()); StringBuilder setSrc = new StringBuilder(); setSrc.append("\tpublic void set" +StringUtils.firstChar2UpperCase(column.getName())+"(" ); setSrc.append(javaFieldType+" " +column.getName()+"){\n" ); setSrc.append("\t\tthis. " +column.getName()+"=" +column.getName()+";\n" ); setSrc.append("\t}\n" ); jfjs.setSetInfo(setSrc.toString()); return jfjs; } public static String createJavaSrc (TableInfo tableInfo,TypeConvertor convertor) { Map<String, ColumnInfo> columns= tableInfo.getColumns(); List<JavaFieldGetSet> javaFields = new ArrayList<JavaFieldGetSet>(); for (ColumnInfo c:columns.values()) { javaFields.add(createFieldGetSetSRC(c, convertor)); } StringBuilder src = new StringBuilder(); src.append("package " +DBManager.getConf().getPoPackage()+";\n\n" ); src.append("import java.sql.*;\n" ); src.append("import java.util.*;\n\n" ); src.append("public class " +StringUtils.firstChar2UpperCase(tableInfo.getTname())+" {\n\n" ); for (JavaFieldGetSet f:javaFields) { src.append(f.getFieldInfo()); } src.append("\n\n" ); for (JavaFieldGetSet f:javaFields) { src.append(f.getGetInfo()); } for (JavaFieldGetSet f:javaFields) { src.append(f.getSetInfo()); } src.append("}" ); return src.toString(); } public static void createJavaPOFile (TableInfo tableInfo,TypeConvertor convertor) { String src = createJavaSrc(tableInfo, convertor); String srcPath = DBManager.getConf().getSrcPath()+"/" ; String packagePath = DBManager.getConf().getPoPackage().replaceAll("\\." , "/" ); File f = new File(srcPath+packagePath); if (!f.exists()) { f.mkdirs(); } BufferedWriter bw = null ; try { bw = new BufferedWriter(new FileWriter(f.getAbsolutePath()+"/" +StringUtils.firstChar2UpperCase(tableInfo.getTname())+".java" )); bw.write(src); System.out.println("建立表" +tableInfo.getTname()+ "对应的java类:" +StringUtils.firstChar2UpperCase(tableInfo.getTname())+".java" ); } catch (IOException e) { e.printStackTrace(); }finally { if (bw!=null ) { try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void main (String[] args) { Map<String,TableInfo> map = TableContext.tables; for (TableInfo t:map.values()) { createJavaPOFile(t, new MySqlTypeConvertor()); } } }
JDBCUtils类 JDBCUtils类封装了JDBC查询常用的操作,主要是给sql设参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class JDBCUtils { public static void handleParams (PreparedStatement ps,Object[] params) { if (params!=null ) { for (int i =0 ;i<params.length;i++) { try { ps.setObject(1 +i, params[i]); } catch (SQLException e) { e.printStackTrace(); } } } } }
ReflectUtils类 ReflectUtils类封装了反射常用的操作,反射这一块也不是很熟悉,后续需要深入学习。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class ReflectUtils { public static Object invokeGet (String fieldName,Object obj) { try { Class c = obj.getClass(); Method m = c.getDeclaredMethod("get" +StringUtils.firstChar2UpperCase(fieldName), null ); return m.invoke(obj, null ); } catch (Exception e) { e.printStackTrace(); return null ; } } public static void invokeSet (Object obj,String columnName,Object columnValue) { Method m; try { if (columnValue!=null ) { m = obj.getClass().getDeclaredMethod("set" +StringUtils.firstChar2UpperCase(columnName), columnValue.getClass()); m.invoke(obj, columnValue); } } catch (Exception e) { e.printStackTrace(); } } }
StringUtils类 StringUtils类封装了字符串常用的操作,主要提供了将目标字符串首字母变为大写的方法,这个方法会经常用到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class StringUtils { public static String firstChar2UpperCase (String str) { return str.toUpperCase().substring(0 ,1 )+str.substring(1 ); } }
配置文件 db.properties 在这里由于我是进行的MySql数据库连接,因此驱动类都是MySql的,如果需要接入Oracle数据库的,修改配置信息以及导入驱动即可。
1 2 3 4 5 6 7 8 9 10 driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql: user=root pwd=Jq576163960 usingDB=mysql srcPath=/Users/jinqi/Desktop/SORM/src poPackage=com.jinqi.po queryClass=com.jinqi.sorm.core.MySqlQuery poolMinSize:10 poolMaxSize:100
使用说明 至此,整个框架的结构都已经总结结束了,这个框架理解起来并不难,后续还可以加入其它如XML解析等,让它更加优化。最后说一下使用说明,需要导入SROM1.0的jar包以及MySql连接的jar包,注意导入的MySql连接的jar包要与本地安装的MySql版本一致,否则会连接失败,更多细节可以查看API文档以及框架源码。