ORM框架:
• 我们希望设计一个可以实现对象和SQL自动映射的框架,但是整体用法和设计比Hibernate简单。砍掉不必要的功能。
• 会穿插使用设计模式
• 增加
– 将对象对应成sql语句,执行sql,插入数据库中
• 删除
– 根据对象主键的值,生成sql,执行,从库中删除
• 修改
– 根据对象需要修改属性的值,生成sql,执行• 查询
– 根据结果分类:
• 多行多列:List<Javabean>
• 一行多列:Javabean
• 一行一列:
– 普通对象:Object
– 数字:Number
基本思路:
获取db.properties配置的相关配置信息
将数据库的表转为对应的java实体类
封装表的信息及对应的java类型
利用反射获取类的相关信息/调用get/set方法
Class实例化对象
创建连接池
封装查询语句
核心架构:
Query接口:
负责查询(对外提供服务的核心类
package com.mikey.core; import com.mikey.bean.ColumnInfo; import com.mikey.bean.TableInfo; import com.mikey.util.JDBCUtil; import com.mikey.util.RefUtil; import java.lang.reflect.Field; import java.sql.*; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 15:30 * @Describe: **/ public abstract class Query implements Cloneable{ /** * 采用模板方法模式将JDBC操作封装成模板,便于重用 * @param sql * @param params * @param clazz * @param callBack * @return */ public Object executeQueryTemplate(String sql, Object[] params, Class clazz, CallBack callBack){ Connection connection=DBManager.getConnection(); PreparedStatement preparedStatement=null; ResultSet resultSet=null; try { preparedStatement=connection.prepareStatement(sql); JDBCUtil.handleParams(preparedStatement,params); System.out.println(preparedStatement); resultSet=preparedStatement.executeQuery(); return callBack.doExecute(connection,preparedStatement,resultSet); }catch (Exception e){ e.printStackTrace(); return null; }finally { DBManager.close(preparedStatement,connection); } } /** * 执行一个DML语句 * @param sql * @param params * @return 执行sql语句后影响的行数 */ public int executeDML(String sql,Object[] params){ Connection connection=DBManager.getConnection(); int count=0; PreparedStatement preparedStatement=null; try { preparedStatement=connection.prepareStatement(sql); JDBCUtil.handleParams(preparedStatement,params); System.out.println(preparedStatement); count=preparedStatement.executeUpdate(); }catch (Exception e){ e.printStackTrace(); }finally { DBManager.close(preparedStatement,connection); } return count; } /** * 将一个对象存储到数据库中 * @param obj */ public void insert(Object obj){ /** * 获取传入对象的Class */ Class clazz=obj.getClass(); /** * 存储sql的参数对象 */ ArrayList<Object> params = new ArrayList<>(); /** * 表信息 */ TableInfo tableInfo=TableContext.poClassTableMap.get(clazz); /** * 构建sql */ StringBuilder sql = new StringBuilder("insert into " + tableInfo.getTname() + " ("); /** * 计算不为null的属性值 */ int countNotNullField=0; /** * 通过反射获取所有属性 */ Field[] fileds=clazz.getDeclaredFields(); /** * 遍历构建SQL */ for (Field field:fileds) { String fieldName=field.getName(); Object fieldValue= RefUtil.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()); } /** * 删除clazz表示类对应的表中的记录(指定主键值id的记录) * @param clazz * @param id */ public void delete(Class clazz,Object id){ //Emp.class,2-->delete from emp where id=2 //通过Class对象找TableInfo TableInfo tableInfo=TableContext.poClassTableMap.get(clazz); //获取主键 ColumnInfo onlyPriKey=tableInfo.getOnlyPrikey(); //sql String sql = "delete from "+tableInfo.getTname()+ " where "+onlyPriKey.getName()+"=?"; //execute executeDML(sql,new Object[]{id}); } /** * 删除对象在数据库中对应的记录(对象所在的类对应到表,对象的主键的值对应到记录) * @param obj */ public void delete(Object obj){ Class clazz=obj.getClass(); TableInfo tableInfo=TableContext.poClassTableMap.get(clazz); ColumnInfo onlyPrikey=tableInfo.getOnlyPrikey(); Object prikeyValue=RefUtil.invokeGet(onlyPrikey.getName(),obj); delete(clazz,prikeyValue); } /** * 更新对象对应的记录 * @param obj * @param fieldNames * @return */ public int update(Object obj,String[] fieldNames){ //obj{"uanme","pwd"}-->update 表名 set uname=?,pwd=? where id=? Class clazz=obj.getClass(); List<Object> params=new ArrayList<>(); TableInfo tableInfo=TableContext.poClassTableMap.get(clazz); ColumnInfo onlyPrikey = tableInfo.getOnlyPrikey(); StringBuilder sql = new StringBuilder("update "+tableInfo.getTname()+ " set "); for (String fname: fieldNames) { Object fvalue=RefUtil.invokeGet(fname,obj); params.add(fvalue); sql.append(fname+"=?,"); } sql.setCharAt(sql.length()-1,' '); sql.append(" where "); sql.append(onlyPrikey.getName()+"=? "); params.add(RefUtil.invokeGet(onlyPrikey.getName(),obj)); return executeDML(sql.toString(),params.toArray()); } /** * 查询返回多行记录,并将每行记录封装到clazz指定的类的对象中 * @param sql * @param clazz * @param params * @return */ public List queryRows(String sql,Class clazz,Object[] params){ return (List)executeQueryTemplate(sql,params,clazz, new CallBack() { @Override public Object doExecute(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) { List list=null; try { ResultSetMetaData metaData= resultSet.getMetaData(); //多行 while (resultSet.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=resultSet.getObject(i+1); RefUtil.invokeSet(rowObj,columnName,columnValue); } list.add(rowObj); } }catch (Exception e){ e.printStackTrace(); } return list; } }); } /** * 查询一行记录 * 查询返回一行记录,并将该记录封装到clazz指定的类的对象中 * @param sql * @param clazz * @param params * @return */ 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); } /** * 查询一个值 * 根据主键的值直接查找对应的对象 * @param sql * @param params * @return */ public Object queryVlaue(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; } }); } /** * 查询一个数值 * 查询返回一个数字(一行一列),并将该值返回 * @param sql * @param clazz * @param params * @return */ public Number queryNumber(String sql,Class clazz,Object[] params){ return (Number)queryVlaue(sql,params); } /** * 分页查询 * @param pageNum 第几页数据 * @param size 每页显示多少记录 * @return */ public abstract Object queryPagenate(int pageNum,int size); @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
QueryFactory类:
负责根据配置信息创建query对象
package com.mikey.core; import com.sun.org.apache.regexp.internal.RE; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 15:42 * @Describe: **/ public class QueryFactory { // public QueryFactory() { // } //原型对象 private static Query prototypeObj; static { try { Class clazz=Class.forName(DBManager.getConf().getQueryClass()); prototypeObj=(Query)clazz.newInstance(); }catch (Exception e){ e.printStackTrace(); } TableContext.loadPOTables(); } /** * 私有化构造器 */ private QueryFactory(){ } /** * createQuery * @return */ private static Query createQuery(){ try { return (Query)prototypeObj.clone(); }catch (Exception e){ e.printStackTrace(); return null; } } }
TypeConvertor接口:
负责类型转换
package com.mikey.core; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 15:43 * @Describe: **/ public interface TypeConvertor { /** * 将数据库类型转换为java类型 * @param columnType * @return */ public String databaseType2JavaType(String columnType); /** * 将java类型转换为数据库类型 * @param javaDataType * @return */ public String javaType2DatabaseType(String javaDataType); }
TableContext类:
负责获取管理数据库所有表结构和类结构的关系,并可以根据表结构生成类结构。
package com.mikey.core; import com.mikey.bean.ColumnInfo; import com.mikey.bean.TableInfo; import com.mikey.util.JavaFileUtil; import com.mikey.util.StringUtil; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 15:49 * @Describe: * 负责获取管理数据库所有表结构和类结构的关系, * 并可以根据表结构生成类结构。 **/ public class TableContext { /** * 表名Key 表信息对象为value */ public static Map<String, TableInfo> tables=new HashMap<String, TableInfo>(); /** * 将po的class的对象和表信息对象关联起来便于重用! */ public static Map<Class,TableInfo> poClassTableMap=new HashMap<Class, TableInfo>(); private TableContext(){} static { try { Connection connection=DBManager.getConnection(); DatabaseMetaData metaData=connection.getMetaData(); ResultSet tableRet=metaData.getTables(null,"%","%",new String[]{"TABLE"}); while (tableRet.next()){ String tableName=(String)tableRet.getObject("TABLE_NAME"); TableInfo tableInfo=new TableInfo(tableName,new HashMap<String,ColumnInfo>(),new ArrayList<ColumnInfo>()); tables.put(tableName,tableInfo); ResultSet set=metaData.getColumns(null,"%",tableName,"%"); while(set.next()){ ColumnInfo ci = new ColumnInfo(set.getString("COLUMN_NAME"), set.getString("TYPE_NAME"), 0); tableInfo.getColumnInfoMap().put(set.getString("COLUMN_NAME"), ci); } ResultSet set2=metaData.getPrimaryKeys(null,"%",tableName); while (set2.next()){ ColumnInfo ci2=(ColumnInfo)tableInfo.getColumnInfoMap().get(set2.getObject("COLUMN_NAME")); ci2.setKeyType(0); tableInfo.getPriKeys().add(ci2); } if (tableInfo.getPriKeys().size()>0){ tableInfo.setOnlyPrikey(tableInfo.getPriKeys().get(0)); } } }catch (Exception e){ e.printStackTrace(); } } /** * 根据表结构,更新配置的po包下面的java类 * 实现了从表结构转化到类结构 */ public static void updateJavaPOFile(){ Map<String,TableInfo> map=TableContext.tables; for (TableInfo t:map.values()) { JavaFileUtil.createJavaPOFile(t,new MySqlTypeConvertor()); } } public static void loadPOTables() { for (TableInfo tableInfo : tables.values()) { try { Class clazz = Class.forName(DBManager.getConf().getPoPackage() + "." + StringUtil.firstChar2UpperCase(tableInfo.getTname())); poClassTableMap.put(clazz, tableInfo); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args){ Map<String, TableInfo> tables = TableContext.tables; System.out.println(tables); } }
DBManager类:
根据配置信息,维持连接对象的管理(增加连接池功能)
package com.mikey.core; import com.mikey.bean.Configuration; import com.mikey.pool.DBConnPool; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.Properties; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 15:50 * @Describe: 根据配置信息 **/ 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 (Exception 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"))); } /** * 获取连接对象 * @return */ public static Connection getConnection(){ if (pool==null){ pool=new DBConnPool(); } return pool.getConnection(); } /** * 创建连接 * @return */ public static Connection createConnection(){ try { Class.forName(conf.getDriver()); return DriverManager.getConnection(conf.getUrl(),conf.getUser(),conf.getPwd()); }catch (Exception e){ e.printStackTrace(); return null; } } /** * 关闭传入的相关资源对象 * @param resultSet * @param statement * @param connection */ public static void close(ResultSet resultSet, Statement statement,Connection connection){ try { if (resultSet!=null){ resultSet.close(); } }catch (Exception e){ e.printStackTrace(); } try { if (statement!=null){ statement.close(); } }catch (Exception e){ e.printStackTrace(); } pool.close(connection); } /** * 关闭Statement返回连接对象到连接池 * @param statement * @param connection */ public static void close(Statement statement,Connection connection){ try { if (statement!=null){ statement.close(); } }catch (Exception e){ e.printStackTrace(); } pool.close(connection); } /** * 返回连接对象到连接池 * @param connection */ public static void close(Connection connection){ pool.close(connection); } /** * 返回Configuration对象 * @return */ public static Configuration getConf(){ return conf; } }
接口回调:
package com.mikey.core; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 16:46 * @Describe: **/ public interface CallBack { public Object doExecute(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet); }
package com.mikey.core; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 17:10 * @Describe: **/ public class MySqlQuery extends Query { }
package com.mikey.core; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 18:04 * @Describe: **/ public class MySqlTypeConvertor implements TypeConvertor{ @Override public String databaseType2JavaType(String columnType) { //varchar-->String 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; } }
连接池:
package com.mikey.pool; import com.mikey.core.DBManager; import java.sql.Connection; import java.util.ArrayList; import java.util.List; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 19:39 * @Describe:连接池 **/ public class DBConnPool { /** * 连接池对象 */ private List<Connection> pool; /** * 最大连接数 */ public static final int POOL_MAX_SIZE= DBManager.getConf().getPoolMaxSize(); /** * 最小连接数 */ public 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.createConnection()); System.out.println("初始化数据库连接池:"+pool.size()); } } /** * 从连接池中取出一个连接 * @return */ public synchronized Connection getConnection(){ int last_index=pool.size()-1; Connection connection=pool.get(last_index); pool.remove(last_index); return connection; } /** * 将连接放回池中 * @param connection */ public synchronized void close(Connection connection){ if (pool.size()>=POOL_MAX_SIZE){ try { if (connection!=null){ connection.close(); } }catch (Exception e){ e.printStackTrace(); } }else { pool.add(connection); } } /** * 构造器初始化 */ public DBConnPool(){ initPool(); } }
工具类:
JDBCUtils封装常用JDBC操作
package com.mikey.util; import java.sql.PreparedStatement; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 15:50 * @Describe:封装了JDBC查询常用的操作 **/ public class JDBCUtil { public static void handleParams(PreparedStatement preparedStatement,Object[] params) { if (params!=null){ for (int i = 0; i < params.length; i++) { try { preparedStatement.setObject(1+i,params[i]); }catch (Exception e){ e.printStackTrace(); } } } } }
JavaFileUtils封装java文件操作
package com.mikey.util; import com.mikey.bean.ColumnInfo; import com.mikey.bean.JavaFieldGetSet; import com.mikey.bean.TableInfo; import com.mikey.core.DBManager; import com.mikey.core.MySqlTypeConvertor; import com.mikey.core.TableContext; import com.mikey.core.TypeConvertor; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 15:52 * @Describe:封装了生成Java文件(源代码)常用的操作 **/ public class JavaFileUtil { /** * 根据字段信息生成java属性信息。如:varchar username-->private String username;以及相应的set和get方法源码 * @param columnInfo * @param typeConvertor * @return */ public static JavaFieldGetSet createFieldGetSetSRC(ColumnInfo columnInfo, TypeConvertor typeConvertor){ JavaFieldGetSet jfgs = new JavaFieldGetSet(); String javaFieldType = typeConvertor.databaseType2JavaType(columnInfo.getDataType()); jfgs.setFieldInfo(" private "+javaFieldType +" "+columnInfo.getName()+"; "); //public String getUsername(){return username;} //生成get方法的源代码 StringBuilder stringBuilde=new StringBuilder(); stringBuilde.append(" public "+javaFieldType+" get"+StringUtil.firstChar2UpperCase(columnInfo.getName())+"(){ "); stringBuilde.append(" return "+columnInfo.getName()+"; "); stringBuilde.append(" } "); jfgs.setGetInfo(stringBuilde.toString()); //public void setUsername(String username){this.username=username;} //生成set方法的源代码 StringBuilder setSrc = new StringBuilder(); setSrc.append(" public void set"+StringUtil.firstChar2UpperCase(columnInfo.getName())+"("); setSrc.append(javaFieldType+" "+columnInfo.getName()+"){ "); setSrc.append(" this."+columnInfo.getName()+"="+columnInfo.getName()+"; "); setSrc.append(" } "); jfgs.setSetInfo(setSrc.toString()); return jfgs; } public static String createJavaSrc(TableInfo tableInfo,TypeConvertor convertor){ Map<String,ColumnInfo> columns = tableInfo.getColumnInfoMap(); List<JavaFieldGetSet> javaFields = new ArrayList<JavaFieldGetSet>(); for (ColumnInfo columnInfo:columns.values()){ javaFields.add(createFieldGetSetSRC(columnInfo,convertor)); } StringBuilder stringBuilder = new StringBuilder(); //生成package stringBuilder.append("package "+ DBManager.getConf().getPoPackage()+"; "); //生成import stringBuilder.append("import java.sql.*; "); stringBuilder.append("import java.util.*; "); //生成类声明语句 stringBuilder.append("public class "+StringUtil.firstChar2UpperCase(tableInfo.getTname())+" { "); //生成属性列表 for (JavaFieldGetSet javaFieldGetSet:javaFields){ stringBuilder.append(javaFieldGetSet.getFieldInfo()); } stringBuilder.append(" "); //生成get方法 for (JavaFieldGetSet javaFieldGetSet:javaFields){ stringBuilder.append(javaFieldGetSet.getGetInfo()); } //生成set方法 for (JavaFieldGetSet javaFieldGetSet:javaFields){ stringBuilder.append(javaFieldGetSet.getSetInfo()); } stringBuilder.append("} "); return stringBuilder.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 file=new File(srcPath+packagePath); if (!file.exists()){ file.mkdirs(); } BufferedWriter bufferedWriter = null; try { bufferedWriter=new BufferedWriter(new FileWriter(file.getAbsoluteFile()+"/"+StringUtil.firstChar2UpperCase(tableInfo.getTname())+".java")); bufferedWriter.write(src); System.out.println("建立表:"+tableInfo.getTname()+"对应的java类:"+StringUtil.firstChar2UpperCase(tableInfo.getTname()+".java")); }catch (Exception e){ e.printStackTrace(); }finally { try { if (bufferedWriter!=null){ bufferedWriter.close(); } }catch (Exception e){ e.printStackTrace(); } } } public static void main(String[] args){ Map<String,TableInfo> map = TableContext.tables; for (TableInfo tableInfo:map.values()){ createJavaPOFile(tableInfo,new MySqlTypeConvertor()); } } }
StringUtils封装常用字符串操作
package com.mikey.util; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 15:51 * @Describe:封装了字符串常用的操作 **/ public class StringUtil { /** * 将目标字符串首字母变为大写 * @param str * @return */ public static String firstChar2UpperCase(String str){ //abcd--->Abcd //abcd--->ABCD--->Abcd return str.toUpperCase().substring(0,1)+str.substring(1); } }
ReflectUtils封装常用反射操作
package com.mikey.util; import java.lang.reflect.Method; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 15:51 * @Describe:反射工具类 **/ public class RefUtil { /** * 调用obj对象对应属性fieldName的get方法 * @param fieldName * @param obj * @return */ public static Object invokeGet(String fieldName,Object obj){ try { Class clazz=obj.getClass(); Method method=clazz.getMethod("get"+StringUtil.firstChar2UpperCase(fieldName),null); return method.invoke(obj,null); }catch (Exception e){ e.printStackTrace(); return null; } } /** * 调用obj对象对应属性fieldName的set方法 * @param obj * @param columnName * @param columnValue */ public static void invokeSet(Object obj,String columnName,Object columnValue) { try { if (columnValue!=null){ Method method=obj.getClass().getDeclaredMethod("set"+StringUtil.firstChar2UpperCase(columnName),null); method.invoke(obj,columnValue); } }catch (Exception e){ e.printStackTrace(); } } }
核心bean,封装相关数据:
ColumnInfo:封装表中一个字段的信息(字段类型、字段名、键类型)
package com.mikey.bean; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 15:52 * @Describe: * 封装一个字段的信息 **/ 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) { this.name = name; this.dataType = dataType; this.keyType = keyType; } }
Configuration:封装配置文件信息
package com.mikey.bean; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 15:55 * @Describe:管理配置信息 **/ public class Configuration { /** * 驱动类 */ private String driver; /** * jdbc的url */ private String url; /** * 数据库用户名 */ private String user; /** * 数据库密码 */ private String pwd; /** * 正在使用哪个数据库 */ private String usingDB; /** * 项目的源码路径 */ private String srcPath; /** * 扫描生成的java类的包(持久画对象) */ private String poPackage; /** * 项目使用的查询类的包 */ private String queryClass; /** * 连接池中最小的连接数 */ private int poolMinSize; /** * 连接池中最大连接数 */ private int poolMaxSize; public Configuration() { } public Configuration(String driver, String url, String user, String pwd, String usingDB, String srcPath, String poPackage, String queryClass, int poolMinSize, int poolMaxSize) { this.driver = driver; this.url = url; this.user = user; this.pwd = pwd; this.usingDB = usingDB; this.srcPath = srcPath; this.poPackage = poPackage; this.queryClass = queryClass; this.poolMinSize = poolMinSize; this.poolMaxSize = poolMaxSize; } 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; } public String getQueryClass() { return queryClass; } public void setQueryClass(String queryClass) { this.queryClass = queryClass; } 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; } }
TableInfo:封装一张表的信息
package com.mikey.bean; import java.util.List; import java.util.Map; /** * @Program: ORM * @Author: 麦奇 * @Email: 1625017540@qq.com * @Create: 2019-04-06 15:56 * @Describe:表信息 **/ public class TableInfo { //表名 private String tname; //表的所有字段 private Map<String,ColumnInfo> columnInfoMap; //唯一主健 private ColumnInfo onlyPrikey; //联合主键 private List<ColumnInfo> priKeys; public String getTname() { return tname; } public void setTname(String tname) { this.tname = tname; } public Map<String, ColumnInfo> getColumnInfoMap() { return columnInfoMap; } public void setColumnInfoMap(Map<String, ColumnInfo> columnInfoMap) { this.columnInfoMap = columnInfoMap; } public ColumnInfo getOnlyPrikey() { return onlyPrikey; } public void setOnlyPrikey(ColumnInfo onlyPrikey) { this.onlyPrikey = onlyPrikey; } public List<ColumnInfo> getPriKeys() { return priKeys; } public void setPriKeys(List<ColumnInfo> priKeys) { this.priKeys = priKeys; } public TableInfo() { } public TableInfo(String tname, Map<String, ColumnInfo> columnInfoMap, ColumnInfo onlyPrikey) { this.tname = tname; this.columnInfoMap = columnInfoMap; this.onlyPrikey = onlyPrikey; } public TableInfo(String tname, Map<String, ColumnInfo> columnInfoMap, List<ColumnInfo> priKeys) { this.tname = tname; this.columnInfoMap = columnInfoMap; this.priKeys = priKeys; } }
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/sorm user=root pwd=123456 usingDB=mysql srcPath=D:\workspace\SORM\src poPackage=com.mikey.po queryClass=com.mikey.sorm.core.MySqlQuery poolMinSize=10 poolMaxSize=100
架构图:
• 针对SORM框架的说明:
– 核心思想:使用简单、性能高、极易上手!
– 配置文件
• 目前使用资源文件、后期项目复杂后可以增加XML文件配置和注解。
– 类名由表名生成,只有首字母大写有区别,其他无区别
– Java对象的属性由表中字段生成,完全对应
– 目前,只支持表中只有一个主键,联合主键不支持
回调接口
public interface CallBack { public Object doExecute(Connection conn,PreparedStatement ps,ResultSet rs); }
模板方法
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); //给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 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; } }); }
• 使用工厂模式统计管理Query的创建
• 使用克隆模式提高Query对象的创建效率
• 连接池(Connection Pool)
– 就是将Connection对象放入List中,反复重用!
– 连接池的初始化:
• 事先放入多个连接对象。
– 从连接池中取连接对象
• 如果池中有可用连接,则将池中最后一个返回。
同时,将该连接从池中remove,表示正在使用。
• 如果池中无可用连接,则创建一个新的。
– 关闭连接
• 不是真正关闭连接,而是将用完的连接放入池中。
• 市面上的连接池产品:
– DBCP
– c3p0
– proxool
实现代码:
代码结构: