元数据指的是”数据库“、“表“、”列“的定义信息。
Connection.getDatabaseMetaData()获得代表DatabaseMetaData元数据的DatabaseMetaData对象。
DataBaseMetaData对象的常用方法:
/** * @Method: testDataBaseMetaData * @Description: 获取数据库的元信息 * @Anthor:孤傲苍狼 * * @throws SQLException */
@Test
public void testDataBaseMetaData() throws SQLException {
Connection conn = JdbcUtils.getConnection();
DatabaseMetaData metadata = conn.getMetaData();
//getURL():返回一个String类对象,代表数据库的URL
System.out.println(metadata.getURL());
//getUserName():返回连接当前数据库管理系统的用户名
System.out.println(metadata.getUserName());
//getDatabaseProductName():返回数据库的产品名称
System.out.println(metadata.getDatabaseProductName());
//getDatabaseProductVersion():返回数据库的版本号
System.out.println(metadata.getDatabaseProductVersion());
//getDriverName():返回驱动驱动程序的名称
System.out.println(metadata.getDriverName());
//getDriverVersion():返回驱动程序的版本号
System.out.println(metadata.getDriverVersion());
//isReadOnly():返回一个boolean值,指示数据库是否只允许读操作
System.out.println(metadata.isReadOnly());
JdbcUtils.release(conn, null, null);
}
运行结果如下:
PreparedStatement.getParameterMetaData() 获得代表PreparedStatement元数据的ParameterMetaData对象。
Select * from user where name=? And password=?
ParameterMetaData对象的常用方法:
/** * @Method: testParameterMetaData * @Description: 获取参数元信息 * @Anthor:孤傲苍狼 * * @throws SQLException */
@Test
public void testParameterMetaData() throws SQLException {
Connection conn = JdbcUtils.getConnection();
String sql = "select * from user wherer name=? and password=?";
//将SQL预编译一下
PreparedStatement st = conn.prepareStatement(sql);
ParameterMetaData pm = st.getParameterMetaData();
//getParameterCount() 获得指定参数的个数
System.out.println(pm.getParameterCount());
//getParameterType(int param):获得指定参数的sql类型,MySQL数据库驱动不支持
System.out.println(pm.getParameterType(1));
JdbcUtils.release(conn, null, null);
}
ResultSet. getMetaData() 获得代表ResultSet对象元数据的ResultSetMetaData对象。
ResultSetMetaData对象的常用方法:
/** * @Method: testResultSetMetaData * @Description: 结果集的元数据 * @Anthor:孤傲苍狼 * * @throws Exception */
@Test
public void testResultSetMetaData() throws Exception {
Connection conn = JdbcUtils.getConnection();
String sql = "select * from account";
PreparedStatement st = conn.prepareStatement(sql);
ResultSet rs = st.executeQuery();
//ResultSet.getMetaData()获得代表ResultSet对象元数据的ResultSetMetaData对象
ResultSetMetaData metadata = rs.getMetaData();
//getColumnCount() 返回resultset对象的列数
System.out.println(metadata.getColumnCount());
//getColumnName(int column) 获得指定列的名称
System.out.println(metadata.getColumnName(1));
//getColumnTypeName(int column)获得指定列的类型
System.out.println(metadata.getColumnTypeName(1));
JdbcUtils.release(conn, st, rs);
}
系统中所有实体对象都涉及到基本的CRUD操作
所有实体的CUD操作代码基本相同,仅仅发送给数据库的SQL语句不同而已,因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的SQL语句。
实体的R操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。
定义一个JdbcUtils工具类,工具类负责获取数据库连接,释放资源,执行SQL的update和query操作,代码如下:
package me.gacl.util;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static{
try{
//读取db.properties文件中的数据库连接信息
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties prop = new Properties();
prop.load(in);
//获取数据库连接驱动
driver = prop.getProperty("driver");
//获取数据库连接URL地址
url = prop.getProperty("url");
//获取数据库连接用户名
username = prop.getProperty("username");
//获取数据库连接密码
password = prop.getProperty("password");
//加载数据库驱动
Class.forName(driver);
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
/** * @Method: getConnection * @Description: 获取数据库连接对象 * @Anthor:孤傲苍狼 * * @return Connection数据库连接对象 * @throws SQLException */
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url, username,password);
}
/** * @Method: release * @Description: 释放资源, * 要释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象 * @Anthor:孤傲苍狼 * * @param conn * @param st * @param rs */
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
//关闭存储查询结果的ResultSet对象
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
//关闭负责执行SQL命令的Statement对象
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
//关闭Connection数据库连接对象
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
/** * @Method: update * @Description: 万能更新 * 所有实体的CUD操作代码基本相同,仅仅发送给数据库的SQL语句不同而已, * 因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的SQL语句 * @Anthor:孤傲苍狼 * @param sql 要执行的SQL * @param params 执行SQL时使用的参数 * @throws SQLException */
public static void update(String sql,Object params[]) throws SQLException{
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = getConnection();
st = conn.prepareStatement(sql);
for(int i=0;i<params.length;i++){
st.setObject(i+1, params[i]);
}
st.executeUpdate();
}finally{
release(conn, st, rs);
}
}
/** * @Method: query * @Description:万能查询 * 实体的R操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同, * 因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。 * @Anthor:孤傲苍狼 * * @param sql 要执行的SQL * @param params 执行SQL时使用的参数 * @param rsh 查询返回的结果集处理器 * @return * @throws SQLException */
public static Object query(String sql,Object params[],ResultSetHandler rsh) throws SQLException{
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = getConnection();
st = conn.prepareStatement(sql);
for(int i=0;i<params.length;i++){
st.setObject(i+1, params[i]);
}
rs = st.executeQuery();
/** * 对于查询返回的结果集处理使用到了策略模式, * 在设计query方法时,query方法事先是无法知道用户对返回的查询结果集如何进行处理的,即不知道结果集的处理策略, * 那么这个结果集的处理策略就让用户自己提供,query方法内部就调用用户提交的结果集处理策略进行处理 * 为了能够让用户提供结果集的处理策略,需要对用户暴露出一个结果集处理接口ResultSetHandler * 用户只要实现了ResultSetHandler接口,那么query方法内部就知道用户要如何处理结果集了 */
return rsh.handler(rs);
}finally{
release(conn, st, rs);
}
}
}
在设计query方法时,对于查询返回的结果集处理使用到了策略模式,query方法事先是无法知道用户对返回的查询结果集如何进行处理的,即不知道结果集的处理策略, 那么这个结果集的处理策略就让用户自己提供,query方法内部就调用用户提交的结果集处理策略进行处理, 为了能够让用户提供结果集的处理策略,需要对用户暴露出一个结果集处理接口ResultSetHandler, 结果集处理器接口ResultSetHandler的定义如下:
package me.gacl.util;
import java.sql.ResultSet;
/** * @ClassName: ResultSetHandler * @Description:结果集处理器接口 * @author: 孤傲苍狼 * @date: 2014-10-5 下午12:01:27 * */
public interface ResultSetHandler {
/** * @Method: handler * @Description: 结果集处理方法 * @Anthor:孤傲苍狼 * * @param rs 查询结果集 * @return */
public Object handler(ResultSet rs);
}
用户只要实现了ResultSetHandler接口,那么就是针对查询结果集写了一个处理器,在query方法内部就调用用户自己写的处理器处理结果集。
为了提高框架的易用性,我们可以事先就针对结果集写好一些常用的处理器,比如将结果集转换成bean对象的处理器,将结果集转换成bean对象的list集合的处理器。
2.2.1、BeanHandler——将结果集转换成bean对象的处理器
package me.gacl.util;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
/** * @ClassName: BeanHandler * @Description: 将结果集转换成bean对象的处理器 * @author: 孤傲苍狼 * @date: 2014-10-5 下午12:00:33 * */
public class BeanHandler implements ResultSetHandler {
private Class<?> clazz;
public BeanHandler(Class<?> clazz){
this.clazz = clazz;
}
public Object handler(ResultSet rs) {
try{
if(!rs.next()){
return null;
}
Object bean = clazz.newInstance();
//得到结果集元数据
ResultSetMetaData metadata = rs.getMetaData();
int columnCount = metadata.getColumnCount();//得到结果集中有几列数据
for(int i=0;i<columnCount;i++){
String coulmnName = metadata.getColumnName(i+1);//得到每列的列名
Object coulmnData = rs.getObject(i+1);
Field f = clazz.getDeclaredField(coulmnName);//反射出类上列名对应的属性
f.setAccessible(true);
f.set(bean, coulmnData);
}
return bean;
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
2.2.2、BeanListHandler——将结果集转换成bean对象的list集合的处理器
package me.gacl.util;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
/** * @ClassName: BeanListHandler * @Description: 将结果集转换成bean对象的list集合的处理器 * @author: 孤傲苍狼 * @date: 2014-10-5 下午12:00:06 * */
public class BeanListHandler implements ResultSetHandler {
private Class<?> clazz;
public BeanListHandler(Class<?> clazz){
this.clazz = clazz;
}
public Object handler(ResultSet rs) {
try{
List<Object> list = new ArrayList<Object>();
while(rs.next()){
Object bean = clazz.newInstance();
ResultSetMetaData metadata = rs.getMetaData();
int count = metadata.getColumnCount();
for(int i=0;i<count;i++){
String name = metadata.getColumnName(i+1);
Object value = rs.getObject(name);
Field f = bean.getClass().getDeclaredField(name);
f.setAccessible(true);
f.set(bean, value);
}
list.add(bean);
}
return list.size()>0?list:null;
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
当框架自身提供的结果集处理器不满足用户的要求时,那么用户就可以自己去实现ResultSetHandler接口,编写满足自己业务要求的结果集处理器。
有了上述的JdbcUtils框架之后,针对单个实体对象CRUD操作就非常方便了,如下所示:
package me.gacl.dao;
import java.sql.SQLException;
import java.util.List;
import me.gacl.domain.Account;
import me.gacl.util.BeanHandler;
import me.gacl.util.BeanListHandler;
import me.gacl.util.JdbcUtils;
public class AccountDao {
public void add(Account account) throws SQLException{
String sql = "insert into account(name,money) values(?,?)";
Object params[] = {account.getName(),account.getMoney()};
JdbcUtils.update(sql, params);
}
public void delete(int id) throws SQLException{
String sql = "delete from account where id=?";
Object params[] = {id};
JdbcUtils.update(sql, params);
}
public void update(Account account) throws SQLException{
String sql = "update account set name=?,money=? where id=?";
Object params[] = {account.getName(),account.getMoney(),account.getId()};
JdbcUtils.update(sql, params);
}
public Account find(int id) throws SQLException{
String sql = "select * from account where id=?";
Object params[] = {id};
return (Account) JdbcUtils.query(sql, params, new BeanHandler(Account.class));
}
public List<Account> getAll() throws SQLException{
String sql = "select * from account";
Object params[] = {};
return (List<Account>) JdbcUtils.query(sql, params,new BeanListHandler(Account.class));
}
}
编写的这个JDBC框架就是模拟Apache的DBUtils框架的实现,下一篇将具体介绍Apache的DBUtils框架。