介绍下在数据库的相关操作中什么是元数据,以及如果自己写工具类的时候应该如何写,或者可以使用别人写好的工具类,比如 Apache 的 dbutils 工具类,就是不知道现在是不是还在用,也当作是为以后学习框架做准备吧!
元数据
首先,来了解下什么是元数据,当然主要说的是数据库相关的,简单说它就是:数据库、表、列的定义信息
所以,指的基本就是数据库一些“对象”的信息,基本可以分为三类
- DatabaseMetaData
数据库的元数据,封装了数据库的相关信息,比如版本、名字、驱动、URL、用户名等等 - ParameterMetaData
参数元数据,简单说就是:获得预编译 SQL 语句中 “?” 信息。比如:参数的个数、类型等,但是并不是所有的数据库(驱动)都支持 - ResultSetMetaData
结果集的元数据,封装了结果集中的列数、列名称、列类型等信息
工具类写法
下面就来优雅的写 JDBC 的工具类,嗯….起码算比较优雅
以前我们在工具类就写了两个方法,一个是获取连接,一个是释放相关资源;下面就来扩展一下,让其变得更加通用一些
增加两个方法,一个用于增删改(update),一个用于查询(query)
1 | public static void update(String sql, Object[] params) throws SQLException { |
基本思路应该看出来了,增删改的时候统一调用 update 方法,传入相应的 SQL 语句和参数数组就可以了;
然后查询的时候单独一个方法 query ,除了需要 SQL 和参数数组还要传入一个 ResultSetHandler 对象,这个是个接口,是自己定义的,主要用于回调来处理结果集,因为 SQL 语句不确定,结果集也不确定如何处理,最妥的方式就是交给用户处理,既然是你写的 SQL 所以你肯定知道怎么处理结果集嘛!
1 | public interface ResultSetHandler { |
当然,如果让用户去实现接口多少还是有些麻烦的,为了更好的用户体验我们可以给用户准备几个常用的,比如把查询出来的数据封装到一个 Javabean 中去:
1 | public class BeanHandler implements ResultSetHandler { |
这里就用到了之前说的元数据,还使用到了反射技术,调用的时候直接 new 这个实现类就可以了,传入一个 class 用来指定 bean 的类型;
当然如果查询返回了多条记录,一般是封装到 List 集合中去,所以再写一个实现类吧:
1 | public class BeanListHandler implements ResultSetHandler { |
然后调用的时候就很简单了:
1 | private static void extendedTest2() throws SQLException { |
使用DBUtils
上面的工具类是我们自己定义的,然后 Apache 其实也写了一个,就叫 dbutils 不过我看了下最新的也已经是几年前了,不知道在实际开发中还用不用,不过嘛,这种工具类应该一般也不需要更新
dbutils 的使用和自定义的工具类很类似,实现原理可能也差不多
它只是对 JDBC 进行简单封装,学习成本低,并且不会影响程序的性能。它不是一个框架
简单使用
使用很简单,毕竟就是为了简化开发,导入相应的包后直接用就行了,主要用到的是 QueryRunner 对象,具体的方法可以去看 API,说点常用的,比如执行增删改操作:
1 | private static void test1() throws SQLException { |
和我们自己写的非常相似,new 的时候接收一个 conn 对象,直接从连接池里取了
再说查询,也是支持封装到 bean 和 List 中去,就举个封装到 List 中的栗子:
1 | private static void test2() throws SQLException { |
dbutils 还给了一些实现了 ResultSetHandler 的类,这里不多说了 API 写的很明白,比如可以封装到数组中去。
如果返回的是单条数据可以使用 ScalarHandler 对象,用于获取结果集中第一行某列的数据并转换成 T (指定的泛型)表示的实际对象,更多的返回类型可以参考:http://www.cnblogs.com/myit/p/4272824.html
批量操作
就是批量执行 SQL 了,当然是相同的 sql 类型,比如可以批量删除、添加
1 | private static void test3() throws SQLException { |
主要区别就是参数的一维数组换成了二维数组,也就是说数组中的每一个数组中封装了批量操作中的一条数据,然后执行 batch 方法执行
事务操作
事务这是一个重要的操作,就当是操作吧,既然要使用事务,那么就不能让它执行完一条 SQL 就关闭连接了,所以只能我们自己传入 conn 了,并且是关闭了自动提交的 conn
1 | private static void test4() throws SQLException { |
当然这样的写法是有问题的,如果是按照三层架构开发的话,dao 层是不允许有业务逻辑的,只能有执行 sql 的相关代码,类似这样的:
1 | // 由构造方法获得 |
这样写的话,如果使用到事务,在 Server 层的时候获取连接后关闭自动提交,然后再传进来,最后还要记得手动 commit 和 close
但是这样写还是不太好,更优雅的写法可以考虑使用 ThreadLocal ,将 conn 绑定到线程上,这样也不用考虑高并发的问题了
使用ThreadLocal优化工具类
ThreadLocal 可以简单理解为线程管理数据的类,他有两个经常用的方法,get、set 都是静态的,分别表示着往当前线程获取、保存数据
下面就来改造下工具类,主要是添加几个管理 conn 的方法,其他方法不变,就省略了
1 | public class JdbcUtilsC3P0 { |
这样改造后基本就比较优雅了,效率也比较好了
多表操作
这个也不是太难,大体思路是:接受一个 bean 根据里面的具体数据来构造相应的 sql ,比如如果传入的 bean 包含有多个子 bean ;其实对应的就是一对多的关系,先把主 bean 中的基本数据存进去,再把子 bean 拆出来一个个的存,最后加上外键连接起来就 OK 了
当然每一个 bean 都应该对应一个具体的 dao 层操作的方法
PS:set 集合的 addAll() 方法可以把传入的集合拆了,然后再存进自己的 set
代码就不写了….
评论框加载失败,无法访问 Disqus
你可能需要魔法上网~~