自定义数据库框架
自定义数据库框架
我们以前写过的DAO,其中有多少冗余代码!分析一下,找出冗余代码,把共同的部分写成方法,把不同的地方写为方法参数。做成一个工具类,就叫QueryRunner
QueryRunner.java
获取DataSource
private DataSource ds;
public QueryRunner(DataSource ds) { this.ds=ds; } |
增删改:只有SQL语句,以及参数不同,其它都相同;
public void update (String sql,Object []objs) { Connection con=null; PreparedStatement st=null; try { con=ds.getConnection(); st=con.prepareStatement(sql); //得到参数的元数据 ParameterMetaData pmd=st.getParameterMetaData(); //参数的个数 int count=pmd.getParameterCount(); if(count>0) { if(objs==null || objs.length==0){ throw new RuntimeException("参数不能为空"); } else if(count!=objs.length){ throw new RuntimeException("参数个数不匹配"); } for (int i = 0; i < count; i++) { st.setObject(i+1, objs[i]); } } st.executeQuery(); } catch (SQLException e) { e.printStackTrace(); } } |
查询:SQL语句不同,参数不同,还有对ResultSet的处理不同!有反把ResultSet映射成Student,还有映射成List<Student>。当然,如果写UserDao,那么还要把ResultSet映射成User或List<User>。
public Object query(String sql,ResultSetHandler resultHandler,Object []objs) { Connection con=null; PreparedStatement st=null; ResultSet rs=null; Object obj=null;
try { con=ds.getConnection(); st=con.prepareStatement(sql);
ParameterMetaData pmd=st.getParameterMetaData(); int count=pmd.getParameterCount(); if(count>0){ if(objs==null||objs.length==0) { System.out.println("参数不能为空"); }else if(objs.length!=count) { System.out.println("参数不匹配"); } for(int i=0;i<count;i++) { st.setObject(i+1, objs[i]); } }
rs=st.executeQuery();
obj = resultHandler.handler(rs); } catch (SQLException e) { e.printStackTrace(); }
return obj; }
|
查询结果集处理
查操作还有一个问题,查操作不只是SQL语句和参数不同,还有把ResultSet映射成什么样子的对象也不同!把结果集映射成什么对象,这应该由用来完成!
因为把结果集映射成一个对象,不是数据,而是动作,那么这种问题就不能通过传递一个普通的参数来处理了,而是传递一个动作给我们的查方法!传递动作就是传递方法,这时你应该写一个接口。
策略模式:把算法提取出来!
public interface ResultSetHandler { Object handler(ResultSet rs); } |
如果结果集是一条记录
注意:可以把结果集转换成Bean对象!要求列名称与Bean属性名称必须一致!
public class BeanHandler implements ResultSetHandler { private Class clz; public BeanHandler (Class<Account> clz) { this.clz=clz; } public Object handler(ResultSet rs) { Object obj=null; try { if(rs.next()) { obj=clz.newInstance(); //获取结果集的元数据 ResultSetMetaData rsmd = rs.getMetaData(); int count = rsmd.getColumnCount();//得到列数 for (int i = 0; i < count; i++) { //列的名字 String columnName=rsmd.getColumnName(i+1); //每一列的值 Object columnValue=rs.getObject(i+1); Field f=clz.getDeclaredField(columnName); f.setAccessible(true); f.set(obj, columnValue); } } } catch (Exception e) { e.printStackTrace(); } return obj; } }
|
结果集是多条记录
public class BeanListHandler implements ResultSetHandler {
private Class clz; public BeanListHandler (Class<Account> clz) { this.clz=clz; } public List handler(ResultSet rs) { List list=new ArrayList(); Object obj=null; try { while(rs.next()) { obj=clz.newInstance(); //获取结果集的元数据 ResultSetMetaData rsmd = rs.getMetaData(); int count = rsmd.getColumnCount();//得到列数 for (int i = 0; i < count; i++) { //列的名字 String columnName=rsmd.getColumnName(i+1); //每一列的值 Object columnValue=rs.getObject(i+1);
Field f=clz.getDeclaredField(columnName); f.setAccessible(true); f.set(obj, columnValue); list.add(obj); } } } catch (Exception e) { e.printStackTrace(); } return list; } }
|
测试类
public class defineFrame { public static void main(String[] args) { QueryRunner qr = new QueryRunner(c3p0Util.getDataSource()); qr.update("insert into account values(?,?,?)",new Object []{6,"aj",1000});
List<Account> account = (List<Account>)qr.query("select * from account", new BeanListHandler(Account.class),null); System.out.println(account); qr.update("delete from account where id=?",new Object []{6}); } } |
ORM简介
1 ORM是什么?
ORM(Object/Relation Mapping)就是对象-关系的映射,对象就是Java这种面向对象语言,关系就是关系型数据库,其实就是把一个对象映射成表的一行记录,再把表的一行记录映射成Java中的一个对象。这就是ORM的用途!
2 常用ORM工具(JDBC框架)
l Apache commons DBUtils:很简单的JDBC框架,很多公司在使用它,就是因为它内容很简单,也很方便;
l Hibernate(全自动):SSH中的H就是它了,它的HQL号称是面向对象的查询语言;
l Ibatis(半自动):简单、方便!很多人用“全自动”形容Hibernate,那么对Ibatis就是“半自动”了。Hibernate把面向关系的东西都封装起来了,甚至你可能对SQL不是很了解都可以通过Hibernate来操作数据库!但是,有是我们还是需要自己来通过面向关系(打开封装)来完成一些特殊的操作,那么“半自动”的Ibatis就派上用场了;
l Spring-JDBC(基本与DBUtils是一个级别的,很简单的封装):Spring中的JDBC框架与dbUtils很相似!但是Spring的IoC给Spring-JDBC做了强大的后盾,并且Spring通过AOP对声明式事务的处理可以说是为人能比,所以,Spring的JDBC框架还是很有用途的;
l EJB(Entity Bean)(老了):Java EE中的实体Bean,因为是重量级组件,现在已经很少使用了。
DBUtils
1 DBUtils简介
DBUtils是Apache Commons组件中的一员,开源免费!
DBUtils是对JDBC的简单封装,但是它还是被很多公司使用!
DBUtils的Jar包:dbutils.jar
2 DBUtils主要类
l DbUtils:都是静态方法,一系列的close()方法;
l QueryRunner:提供update()、query()、batch()方法;
其实我们自定义的框架中,JdbcRunner就是按照QueryRunner来写的,所以大家对update()和query()方法的使用应该没有问题了。
QueryRunner的query()方法也需要ResultSetHandler来映射结果集,接口名称也与我们写的一样,所以大家应该比较熟悉了。
3 QueryRunner之更新
在DBUtlis中最主要的类就是QueryRunner了,创建它有如下两种方式:
QueryRunner qr1 = new QueryRunner(); DataSource ds = … QueryRunner qr2 = new QueryRunner(ds); |
一种方法是给QueryRunner指定DataSource,另一种是不指定DataSource。本来在DBUtils1.0版本就存在的setDataSource()方法,在1.4已经消失了!不能向下兼容的东西,真是垃圾!
QueryRunner的update()和query()方法有两种重要的重载方式:需要Connection参数的,和不需要Connection参数的,需要Connectoin参数的很好理解!不需要Connection参数时,QueryRunner使用DataSource来获取Connection。如果你没有给QueryRunner指定DataSource,那么你就不能使用不需要Connection的update()和query()方法了。
l query(Connection on, String sql, ResultSetHandler rsh, Object… params):需要Connection,不会关闭Connection,这说明调用者自己需要关闭Connection;
l query(String sql, ResultSetHandler rsh, Object… params):不需要Connection,会使用DataSource来获取Connection。如果没有给QueryRunner指定DataSource就会出现异常!会关闭Connection!
l update(Connection on, String sql, Object… params):需要Connection参数,执行完成后,不会关闭Connection,这说明调用者需要自己来关闭Connection;
l update(String sql, Object… params):不需要Connection,会使用DataSource来获取Connection,如果没有为QueryRunner指定DataSource,那么调用本方法就会抛出异常。执行结束后会关闭Connection。
QueryRunner qr = new QueryRunner(); String sql = "insert into tab_student values(?,?,?,?)"; Connection con = ... Object[] params = {...}; qr.update(con, sql, params); con.close(); |
DataSource ds = ... QueryRunner qr = new QueryRunner(ds); String sql = "insert into tab_student values(?,?,?,?)"; Object[] params = {...}; qr.update(sql, params); |
4 QueryRunner之查询
DBUtils为我们提供了一些处理器,即ResultSetHandler的实现类!当然,如果你觉得DBUtils提供的处理器还不够的话,你可以自己再写一些处理器。
l ArrayHandler:单行处理器!把结果集转换成Object[];
l ArrayListHandler:多行处理器!把结果集转换成List<Object[]>;
l MapHandler:单行处理器!把结果集转换成Map<String,Object>,其中列名为键!
l MapListHandler:多行处理器!把结果集转换成List<Map<String,Object>>;
l BeanHandler:单行处理器!把结果集转换成Bean,该处理器需要Class参数,即Bean的类型;
l BeanListHandler:多行处理器!把结果集转换成List<Bean>;
l ColumnListHandler:多行单列处理器!把结果集转换成List<Object>,使用ColumnListHandler时需要指定某一列的名称或编号,例如:new ColumListHandler(“name”)表示把name列的数据放到List中。
l KeyedHandler:多行处理器!把结果集转换成Map<Object,Map<String,Object>>。使用KeyedHandler时需要指定主键列名称或编码,例如:new KeyedHandler(“number”)!生成的Map中以主键列的值为键,值还是一个Map,Map表示当前行记录。
l ScalarHandler:单行单列处理器!把结果集转换成Object。一般用于聚集查询,例如select count(*) from tab_student。
@Test public void fun1() throws SQLException { DataSource ds = JdbcUtils.getDBCPDataSource(); QueryRunner qr = new QueryRunner(ds); String sql = "select * from tab_student where number=?"; Object[] objs = qr.query(sql, new ArrayHandler(), "S_2000"); System.out.println(Arrays.toString(objs)); }
@Test public void fun2() throws SQLException { DataSource ds = JdbcUtils.getDBCPDataSource(); QueryRunner qr = new QueryRunner(ds); String sql = "select * from tab_student"; List<Object[]> list = qr.query(sql, new ArrayListHandler()); for(Object[] objs : list) { System.out.println(Arrays.toString(objs)); } }
@Test public void fun3() throws SQLException { DataSource ds = JdbcUtils.getDBCPDataSource(); QueryRunner qr = new QueryRunner(ds); String sql = "select * from tab_student where number=?"; Map<String,Object> map = qr.query(sql, new MapHandler(), "S_2000"); System.out.println(map); }
@Test public void fun4() throws SQLException { DataSource ds = JdbcUtils.getDBCPDataSource(); QueryRunner qr = new QueryRunner(ds); String sql = "select * from tab_student"; List<Map<String,Object>> list = qr.query(sql, new MapListHandler()); for(Map<String,Object> map : list) { System.out.println(map); } }
@Test public void fun5() throws SQLException { DataSource ds = JdbcUtils.getDBCPDataSource(); QueryRunner qr = new QueryRunner(ds); String sql = "select * from tab_student where number=?"; Student stu = qr.query(sql, new BeanHandler<Student>(Student.class), "S_2000"); System.out.println(stu); }
@Test public void fun6() throws SQLException { DataSource ds = JdbcUtils.getDBCPDataSource(); QueryRunner qr = new QueryRunner(ds); String sql = "select * from tab_student"; List<Student> list = qr.query(sql, new BeanListHandler<Student>(Student.class)); for(Student stu : list) { System.out.println(stu); } }
@Test public void fun7() throws SQLException { DataSource ds = JdbcUtils.getDBCPDataSource(); QueryRunner qr = new QueryRunner(ds); String sql = "select * from tab_student"; List<Object> list = qr.query(sql, new ColumnListHandler("name")); for(Object s : list) { System.out.println(s); } }
@Test public void fun8() throws SQLException { DataSource ds = JdbcUtils.getDBCPDataSource(); QueryRunner qr = new QueryRunner(ds); String sql = "select * from tab_student"; Map<Object,Map<String,Object>> map = qr.query(sql, new KeyedHandler("number")); /*与MapListHandler相似,MapListHandler返回的是List<Map>,而KeyedHandler返回的是Map<Object,Map>,其中键为主键列的值,所以在使用KeyedHandler时需要指定主键列名称 */ for(Map.Entry<Object,Map<String,Object>> entry : map.entrySet()) { System.out.println(entry); } }
@Test public void fun9() throws SQLException { DataSource ds = JdbcUtils.getDBCPDataSource(); QueryRunner qr = new QueryRunner(ds); String sql = "select count(*) from tab_student"; Number number = (Number)qr.query(sql, new ScalarHandler()); /* 单行单列处理器,一般用于聚合查询,在使用ScalarHandler时可以指定列名,如果不指定,默认为第1列。 */ int cnt = number.intValue(); /* 对聚合函数的查询结果,有的驱动返回的是Long,有的返回的是BigInteger,所以这里我们把它转换成Number,Number是Long和BigInteger的父类!然后我们再调用Number的intValue()或longValue()方法就OK了。 */ System.out.println(cnt); } |
5 QueryRunner之批处理
QueryRunner还提供了批处理方法:batch()。
我们更新一行记录时需要指定一个Object[]为参数,如果是批处理,那么就要指定Object[][]为参数了。即多个Object[]就是Object[][]了,其中每个Object[]对应一行记录:
@Test public void fun10() throws SQLException { DataSource ds = JdbcUtils.getDBCPDataSource(); QueryRunner qr = new QueryRunner(ds); String sql = "insert into tab_student values(?,?,?,?)"; Object[][] params = new Object[10][];//表示 要插入10行记录 for(int i = 0; i < params.length; i++) { params[i] = new Object[]{"S_300" + i, "name" + i, 30 + i, i%2==0?"男":"女"}; } qr.batch(sql, params); //执行批处理 } |
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。