我们希望设计一个可以实现对象和 SQL 自动映射的框架,但是整体用法和设计比 Hibernate 简单,砍掉不必要的功能。
会穿插使用设计模式。
增删改:对象——>sql
查询: sql——>对象
-
增加:将对象对应成 sql 语句,执行 sql,插入数据库中
-
删除:根据对象主键的值,生成 sql,执行,从库中删除
-
修改:根据对象需要修改属性的值,生成 sql,执行
-
查询:
- 根据结果分类
- 多行多列:List
- 一行多列:JavaBean
- 一行一列:
- 普通对象:Object
- 数字:Number
- 根据结果分类
-
核心架构
- Query 接口:负责查询(对外提供服务的核心类)
- QueryFacory 类:负责根据配置信息创建 query 对象
- TypeConvertor 接口:负责类型转换
- TableContext 类:负责获取管理数据库所有表结构和类接管的关系,并可以根据表结构生成类结构
- DBManager 类:根据配置信息,维持连接对象的管理(增加连接池功能)
-
工具类
- JDBCUtils 封装常用 JDBC 操作
- StringUtils 封装常用字符串操作
- JavaFileUtils 封装 Java 文件操作
- ReflectUtils 封装常用反射操作
-
核心 Bean,封装相关数据
- ColumnInfo:封装表中一个字段的信息(字段类型、字段名、键类型)
- Configuration:封装配置文件信息
- TableInfo:封装一张表的信息
-
针对 ORM 框架的说明
- 配置文件:目前使用资源文件,后期复杂使用可增加 xml文件配置和注解
- 类名由表名生成,只有首字母大写有区别,其他无区别eg:user——>User
- Java 对象的属性由表中字段生成,完全对应
- 目前,只支持表中只有一个主键,联合主键不支持
-
在 src 下建立 db.properties。
-
每张表只能有一个主键。不能处理多个主键的情况。
-
po 尽量使用包装类,不要使用基本数据类型。
/** * 直接执行一个DML语句 * @param sql sql语句 * @param params 参数 * @return int 执行sql语句后影响记录的行数 */ public int executeDML(String sql, Object[] params); /** * 将一个对象存储到数据库中 * @param obj */ public void insert(Object obj); /** * 删除clazz表示类对应的表中的记录(指定主键值id的记录) * @param clazz 和表对应的类的Class对象 * @param id 主键值 */ public void delete(Class clazz, Object id); //delete from User where id=2; /** * 删除对象在数据库中对应的记录(对象所在的类对应到表,对象的主键值对应到记录) * @param obj */ public void delete(Object obj); /** * 更新对象对应的记录,并且只更新指定字段值 * @param obj 所要更新的对象 * @param fieldNames 更新的属性列表 * @return 执行sql语句后影响记录的行数 */ public int update(Object obj, String[] fieldNames); //update User set uname=?,pwd=? /** * 查询返回多行记录,并将每行记录封装到clazz指定的类对象中 * @param sql 查询语句 * @param clazz 封装数据的JavaBean类的Class对象 * @param params sql的参数 * @return 查询到的结果 */ public List queryRows(String sql, Class clazz, Object[] params);//传入sql,出来对象 /** * 查询返回一行记录,并将该记录封装到clazz指定的类对象中 * @param sql 查询语句 * @param clazz 封装数据的JavaBean类的Class对象 * @param params sql的参数 * @return 查询到的结果 */ public Object queryUniqueRow(String sql, Class clazz, Object[] params);//传入sql,出来对象 /** * 查询返回一个值(一行一列),并将该值返回 * @param sql 查询语句 * @param params sql的参数 * @return 查询到的结果 */ public Object queryValue(String sql, Object[] params);//传入sql,出来对象 /** * 查询返回一个数字(count(*))(一行一列),并将该值返回 * @param sql 查询语句 * @param params sql的参数 * @return 查询到的结果 */ public Number queryNumber(String sql, Object[] params);//传入sql,出来对象
核心增删改查
@Override public int executeDML(String sql, Object[] params) { Connection connection = DBManager.getConnection(); int cnt = 0; PreparedStatement ps = null; try { ps = connection.prepareStatement(sql); //给sql设参 JdbcUtils.handlerParams(ps,params); System.out.println(ps); cnt = ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally { DBManager.close(connection,ps); } return cnt; } @Override public void insert(Object obj) { //obj-->表中 insert into 表名 (id,uname,pwd) values (?,?,?) Class c = obj.getClass(); //存储sql参数对象 List<Object> params = new ArrayList<Object>(); TableInfo tableInfo = TableContext.poClassTableMap.get(c); StringBuilder sql = new StringBuilder("insert into " + tableInfo.getName() + " ("); int countNotNullField = 0; //计算不为空的属性值 Field[] fields = c.getDeclaredFields();//遍历所有属性 for (Field f: fields) { String fieldName = f.getName(); //属性名 Object fieldValue = ReflectUtils.invokeGet(obj,fieldName); 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,')'); System.out.println(sql.toString()); executeDML(sql.toString(),params.toArray()); } @Override 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(); // String sql = "delete from " + tableInfo.getName() + " where " + onlyPriKey.getName() + "=?"; executeDML(sql,new Object[]{id}); } @Override public void delete(Object obj) { //获得clazz Class c = obj.getClass(); TableInfo tableInfo = TableContext.poClassTableMap.get(c); //获得主键 ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey(); //通过反射机制调用对应的get方法或set方法 getUser() Object priKeyValue = ReflectUtils.invokeGet(obj,onlyPriKey.getName()); delete(c,priKeyValue); } @Override public int update(Object obj, String[] fieldNames) { //obj{"uname","pwd"} --> update 表名 set uname=?,pwd=? where id=? Class c = obj.getClass(); //存储sql参数对象 List<Object> params = new ArrayList<Object>(); TableInfo tableInfo = TableContext.poClassTableMap.get(c); ColumnInfo priKey = tableInfo.getOnlyPriKey(); //获得唯一主键 StringBuilder sql = new StringBuilder("update " + tableInfo.getName() + " set "); for (String fname:fieldNames) { Object fvalue = ReflectUtils.invokeGet(obj,fname); params.add(fvalue); sql.append(fname + "=?,"); } sql.setCharAt(sql.length()-1,' '); sql.append("where "); sql.append(priKey.getName() + "=? "); params.add(ReflectUtils.invokeGet(obj,priKey.getName())); System.out.println(sql); return executeDML(sql.toString(),params.toArray()); } @Override public List queryRows(String sql, Class clazz, Object[] params) { Connection connection = DBManager.getConnection(); PreparedStatement ps = null; List<Object> rows = new ArrayList<Object>(); ResultSet resultSet = null; try { ps = connection.prepareStatement(sql); JdbcUtils.handlerParams(ps, params); resultSet = ps.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); while (resultSet.next()) { Object o = clazz.newInstance(); for (int i = 0; i < metaData.getColumnCount(); i++) { //得到每一列的名称 String columnLabel = metaData.getColumnLabel(i + 1); Object columnValue = resultSet.getObject(i + 1); ReflectUtils.invokeSet(o, columnLabel, columnValue); } rows.add(o); } return rows; }catch (Exception e){ e.printStackTrace(); }finally { DBManager.close(connection,ps); } return null; } @Override public Object queryUniqueRow(String sql, Class clazz, Object[] params) { List list = queryRows(sql,clazz,params); return (list == null && list.size() > 0) ? null : list.get(0); } @Override public Object queryValue(String sql, Object[] params) { //select count(*) from user... Connection connection = DBManager.getConnection(); PreparedStatement ps = null; ResultSet resultSet = null; Object o =null; try { ps = connection.prepareStatement(sql); JdbcUtils.handlerParams(ps,params); resultSet = ps.executeQuery(); while (resultSet.next()){ o = resultSet.getObject(1); } } catch (Exception e) { e.printStackTrace(); return null; }finally { DBManager.close(connection,ps); } return o; } @Override public Number queryNumber(String sql, Object[] params) { return (Number) queryValue(sql,params); }
连接池(Connection Pool)
- 就是将 Connection 对象放入 List 中,反复重用
- 连接池的初始化
- 实现放入多个连接对象
- 从连接池中取连接对象
- 如果池中有可用连接,则将池中最后一个返回。同时,将该连接从池中 remove,表示正在使用。
- 如果池中无可用连接,则创建一个新的。
- 关闭连接
- 不是真正的关闭连接,而是将用完的连接放入池中。
- 市面上的连接池产品
- DBCP
- c3p0
- proxool