一、元数据介绍
元数据指的是"数据库"、"表"、"列"的定义信息。
1.1、DataBaseMetaData元数据
Connection.getDatabaseMetaData()获得代表DatabaseMetaData元数据的DatabaseMetaData对象。
DataBaseMetaData对象的常用方法:
- getURL():返回一个String类对象,代表数据库的URL。
- getUserName():返回连接当前数据库管理系统的用户名。
- getDatabaseProductName():返回数据库的产品名称。
- getDatabaseProductVersion():返回数据库的版本号。
- getDriverName():返回驱动驱动程序的名称。
- getDriverVersion():返回驱动程序的版本号。
- isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。
1 /**2 * @Method: testDataBaseMetaData3 * @Description: 获取数据库的元信息4 * @Anthor:孤傲苍狼5 *6 * @throws SQLException7 */ 8 @Test9 public void testDataBaseMetaData() throws SQLException {
10 Connection conn = JdbcUtils.getConnection();
11 DatabaseMetaData metadata = conn.getMetaData();
12 //getURL():返回一个String类对象,代表数据库的URL
13 System.out.println(metadata.getURL());
14 //getUserName():返回连接当前数据库管理系统的用户名
15 System.out.println(metadata.getUserName());
16 //getDatabaseProductName():返回数据库的产品名称
17 System.out.println(metadata.getDatabaseProductName());
18 //getDatabaseProductVersion():返回数据库的版本号
19 System.out.println(metadata.getDatabaseProductVersion());
20 //getDriverName():返回驱动驱动程序的名称
21 System.out.println(metadata.getDriverName());
22 //getDriverVersion():返回驱动程序的版本号
23 System.out.println(metadata.getDriverVersion());
24 //isReadOnly():返回一个boolean值,指示数据库是否只允许读操作
25 System.out.println(metadata.isReadOnly());
26 JdbcUtils.release(conn, null, null);
27 } 运行结果如下:

1.2、ParameterMetaData元数据
PreparedStatement.getParameterMetaData() 获得代表PreparedStatement元数据的ParameterMetaData对象。
Select * from user where name=? And password=?
ParameterMetaData对象的常用方法:
- getParameterCount(): 获得指定参数的个数
- getParameterType(int param):获得指定参数的sql类型,MySQL数据库驱动不支持
1 /**2 * @Method: testParameterMetaData3 * @Description: 获取参数元信息4 * @Anthor:孤傲苍狼5 *6 * @throws SQLException7 */ 8 @Test9 public void testParameterMetaData() throws SQLException {
10 Connection conn = JdbcUtils.getConnection();
11 String sql = "select * from user wherer name=? and password=?";
12 //将SQL预编译一下
13 PreparedStatement st = conn.prepareStatement(sql);
14 ParameterMetaData pm = st.getParameterMetaData();
15 //getParameterCount() 获得指定参数的个数
16 System.out.println(pm.getParameterCount());
17 //getParameterType(int param):获得指定参数的sql类型,MySQL数据库驱动不支持
18 System.out.println(pm.getParameterType(1));
19 JdbcUtils.release(conn, null, null);
20 } 1.3、ResultSetMetaData元数据
ResultSet. getMetaData() 获得代表ResultSet对象元数据的ResultSetMetaData对象。
ResultSetMetaData对象的常用方法:
- getColumnCount() 返回resultset对象的列数
- getColumnName(int column) 获得指定列的名称
- getColumnTypeName(int column)获得指定列的类型
1 /**2 * @Method: testResultSetMetaData3 * @Description: 结果集的元数据4 * @Anthor:孤傲苍狼5 *6 * @throws Exception7 */8 @Test9 public void testResultSetMetaData() throws Exception {
10 Connection conn = JdbcUtils.getConnection();
11 String sql = "select * from account";
12 PreparedStatement st = conn.prepareStatement(sql);
13 ResultSet rs = st.executeQuery();
14 //ResultSet.getMetaData()获得代表ResultSet对象元数据的ResultSetMetaData对象
15 ResultSetMetaData metadata = rs.getMetaData();
16 //getColumnCount() 返回resultset对象的列数
17 System.out.println(metadata.getColumnCount());
18 //getColumnName(int column) 获得指定列的名称
19 System.out.println(metadata.getColumnName(1));
20 //getColumnTypeName(int column)获得指定列的类型
21 System.out.println(metadata.getColumnTypeName(1));
22 JdbcUtils.release(conn, st, rs);
23 } 二、使用元数据封装简单的JDBC框架
系统中所有实体对象都涉及到基本的CRUD操作
所有实体的CUD操作代码基本相同,仅仅发送给数据库的SQL语句不同而已,因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的SQL语句。
实体的R操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。
2.1、封装通用的update方法和qurey方法
定义一个JdbcUtils工具类,工具类负责获取数据库连接,释放资源,执行SQL的update和query操作,代码如下:
1 package me.gacl.util;2 3 import java.io.InputStream;4 import java.sql.Connection;5 import java.sql.DriverManager;6 import java.sql.PreparedStatement;7 import java.sql.ResultSet;8 import java.sql.SQLException;9 import java.sql.Statement;10 import java.util.Properties;11 12 public class JdbcUtils {13 14 private static String driver = null;15 private static String url = null;16 private static String username = null;17 private static String password = null;18 19 static{20 try{21 //读取db.properties文件中的数据库连接信息22 InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");23 Properties prop = new Properties();24 prop.load(in);25 26 //获取数据库连接驱动27 driver = prop.getProperty("driver");28 //获取数据库连接URL地址29 url = prop.getProperty("url");30 //获取数据库连接用户名31 username = prop.getProperty("username");32 //获取数据库连接密码33 password = prop.getProperty("password");34 35 //加载数据库驱动36 Class.forName(driver);37 38 }catch (Exception e) {39 throw new ExceptionInInitializerError(e);40 }41 }42 43 /**44 * @Method: getConnection45 * @Description: 获取数据库连接对象46 * @Anthor:孤傲苍狼47 *48 * @return Connection数据库连接对象49 * @throws SQLException50 */ 51 public static Connection getConnection() throws SQLException{52 return DriverManager.getConnection(url, username,password);53 }54 55 /**56 * @Method: release57 * @Description: 释放资源,58 * 要释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象59 * @Anthor:孤傲苍狼60 *61 * @param conn62 * @param st63 * @param rs64 */ 65 public static void release(Connection conn,Statement st,ResultSet rs){66 if(rs!=null){67 try{68 //关闭存储查询结果的ResultSet对象69 rs.close();70 }catch (Exception e) {71 e.printStackTrace();72 }73 rs = null;74 }75 if(st!=null){76 try{77 //关闭负责执行SQL命令的Statement对象78 st.close();79 }catch (Exception e) {80 e.printStackTrace();81 }82 }83 84 if(conn!=null){85 try{86 //关闭Connection数据库连接对象87 conn.close();88 }catch (Exception e) {89 e.printStackTrace();90 }91 }92 }93 94 /**95 * @Method: update96 * @Description: 万能更新97 * 所有实体的CUD操作代码基本相同,仅仅发送给数据库的SQL语句不同而已,98 * 因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的SQL语句99 * @Anthor:孤傲苍狼
100 * @param sql 要执行的SQL
101 * @param params 执行SQL时使用的参数
102 * @throws SQLException
103 */
104 public static void update(String sql,Object params[]) throws SQLException{
105 Connection conn = null;
106 PreparedStatement st = null;
107 ResultSet rs = null;
108 try{
109 conn = getConnection();
110 st = conn.prepareStatement(sql);
111 for(int i=0;i<params.length;i++){
112 st.setObject(i+1, params[i]);
113 }
114 st.executeUpdate();
115
116 }finally{
117 release(conn, st, rs);
118 }
119 }
120
121 /**
122 * @Method: query
123 * @Description:万能查询
124 * 实体的R操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,
125 * 因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。
126 * @Anthor:孤傲苍狼
127 *
128 * @param sql 要执行的SQL
129 * @param params 执行SQL时使用的参数
130 * @param rsh 查询返回的结果集处理器
131 * @return
132 * @throws SQLException
133 */
134 public static Object query(String sql,Object params[],ResultSetHandler rsh) throws SQLException{
135
136 Connection conn = null;
137 PreparedStatement st = null;
138 ResultSet rs = null;
139
140 try{
141 conn = getConnection();
142 st = conn.prepareStatement(sql);
143 for(int i=0;i<params.length;i++){
144 st.setObject(i+1, params[i]);
145 }
146 rs = st.executeQuery();
147 /**
148 * 对于查询返回的结果集处理使用到了策略模式,
149 * 在设计query方法时,query方法事先是无法知道用户对返回的查询结果集如何进行处理的,即不知道结果集的处理策略,
150 * 那么这个结果集的处理策略就让用户自己提供,query方法内部就调用用户提交的结果集处理策略进行处理
151 * 为了能够让用户提供结果集的处理策略,需要对用户暴露出一个结果集处理接口ResultSetHandler
152 * 用户只要实现了ResultSetHandler接口,那么query方法内部就知道用户要如何处理结果集了
153 */
154 return rsh.handler(rs);
155
156 }finally{
157 release(conn, st, rs);
158 }
159 }
160 } 在设计query方法时,对于查询返回的结果集处理使用到了策略模式,query方法事先是无法知道用户对返回的查询结果集如何进行处理的,即不知道结果集的处理策略, 那么这个结果集的处理策略就让用户自己提供,query方法内部就调用用户提交的结果集处理策略进行处理, 为了能够让用户提供结果集的处理策略,需要对用户暴露出一个结果集处理接口ResultSetHandler, 结果集处理器接口ResultSetHandler的定义如下:
1 package me.gacl.util;2 3 import java.sql.ResultSet;4 5 /**6 * @ClassName: ResultSetHandler7 * @Description:结果集处理器接口8 * @author: 孤傲苍狼9 * @date: 2014-10-5 下午12:01:27
10 *
11 */
12 public interface ResultSetHandler {
13
14 /**
15 * @Method: handler
16 * @Description: 结果集处理方法
17 * @Anthor:孤傲苍狼
18 *
19 * @param rs 查询结果集
20 * @return
21 */
22 public Object handler(ResultSet rs);
23 } 用户只要实现了ResultSetHandler接口,那么就是针对查询结果集写了一个处理器,在query方法内部就调用用户自己写的处理器处理结果集。
2.2、编写常用的结果集处理器
为了提高框架的易用性,我们可以事先就针对结果集写好一些常用的处理器,比如将结果集转换成bean对象的处理器,将结果集转换成bean对象的list集合的处理器。
2.2.1、BeanHandler——将结果集转换成bean对象的处理器
1 package me.gacl.util;2 3 import java.lang.reflect.Field;4 import java.sql.ResultSet;5 import java.sql.ResultSetMetaData;6 7 /**8 * @ClassName: BeanHandler9 * @Description: 将结果集转换成bean对象的处理器
10 * @author: 孤傲苍狼
11 * @date: 2014-10-5 下午12:00:33
12 *
13 */
14 public class BeanHandler implements ResultSetHandler {
15 private Class<?> clazz;
16 public BeanHandler(Class<?> clazz){
17 this.clazz = clazz;
18 }
19 public Object handler(ResultSet rs) {
20 try{
21 if(!rs.next()){
22 return null;
23 }
24 Object bean = clazz.newInstance();
25 //得到结果集元数据
26 ResultSetMetaData metadata = rs.getMetaData();
27 int columnCount = metadata.getColumnCount();//得到结果集中有几列数据
28 for(int i=0;i<columnCount;i++){
29 String coulmnName = metadata.getColumnName(i+1);//得到每列的列名
30 Object coulmnData = rs.getObject(i+1);
31 Field f = clazz.getDeclaredField(coulmnName);//反射出类上列名对应的属性
32 f.setAccessible(true);
33 f.set(bean, coulmnData);
34 }
35 return bean;
36 }catch (Exception e) {
37 throw new RuntimeException(e);
38 }
39 }
40 } 2.2.2、BeanListHandler——将结果集转换成bean对象的list集合的处理器
1 package me.gacl.util;2 3 import java.lang.reflect.Field;4 import java.sql.ResultSet;5 import java.sql.ResultSetMetaData;6 import java.util.ArrayList;7 import java.util.List;8 9 /**
10 * @ClassName: BeanListHandler
11 * @Description: 将结果集转换成bean对象的list集合的处理器
12 * @author: 孤傲苍狼
13 * @date: 2014-10-5 下午12:00:06
14 *
15 */
16 public class BeanListHandler implements ResultSetHandler {
17 private Class<?> clazz;
18 public BeanListHandler(Class<?> clazz){
19 this.clazz = clazz;
20 }
21
22 public Object handler(ResultSet rs) {
23 try{
24 List<Object> list = new ArrayList<Object>();
25 while(rs.next()){
26 Object bean = clazz.newInstance();
27
28 ResultSetMetaData metadata = rs.getMetaData();
29 int count = metadata.getColumnCount();
30 for(int i=0;i<count;i++){
31 String name = metadata.getColumnName(i+1);
32 Object value = rs.getObject(name);
33
34 Field f = bean.getClass().getDeclaredField(name);
35 f.setAccessible(true);
36 f.set(bean, value);
37 }
38 list.add(bean);
39 }
40 return list.size()>0?list:null;
41
42 }catch (Exception e) {
43 throw new RuntimeException(e);
44 }
45 }
46 } 当框架自身提供的结果集处理器不满足用户的要求时,那么用户就可以自己去实现ResultSetHandler接口,编写满足自己业务要求的结果集处理器。
有了上述的JdbcUtils框架之后,针对单个实体对象CRUD操作就非常方便了,如下所示:
1 package me.gacl.dao;2 3 import java.sql.SQLException;4 import java.util.List;5 import me.gacl.domain.Account;6 import me.gacl.util.BeanHandler;7 import me.gacl.util.BeanListHandler;8 import me.gacl.util.JdbcUtils;9
10 public class AccountDao {
11
12 public void add(Account account) throws SQLException{
13 String sql = "insert into account(name,money) values(?,?)";
14 Object params[] = {account.getName(),account.getMoney()};
15 JdbcUtils.update(sql, params);
16 }
17
18
19 public void delete(int id) throws SQLException{
20 String sql = "delete from account where id=?";
21 Object params[] = {id};
22 JdbcUtils.update(sql, params);
23 }
24
25 public void update(Account account) throws SQLException{
26
27 String sql = "update account set name=?,money=? where id=?";
28 Object params[] = {account.getName(),account.getMoney(),account.getId()};
29 JdbcUtils.update(sql, params);
30
31 }
32
33 public Account find(int id) throws SQLException{
34 String sql = "select * from account where id=?";
35 Object params[] = {id};
36 return (Account) JdbcUtils.query(sql, params, new BeanHandler(Account.class));
37 }
38
39 public List<Account> getAll() throws SQLException{
40 String sql = "select * from account";
41 Object params[] = {};
42 return (List<Account>) JdbcUtils.query(sql, params,new BeanListHandler(Account.class));
43 }
44 } 编写的这个JDBC框架就是模拟Apache的DBUtils框架的实现,下一篇将具体介绍Apache的DBUtils框架。
