一、元数据(DataBaseMetaData)
元数据:数据库、表、列的定义信息
Connection.getDatabaseMetaData()
获得元数据DataBaseMetaData对象方法
getURL()
:返回一个String类对象,代表数据库的URL。
getUserName()
:返回连接当前数据库管理系统的用户名。
getDatabaseProductName()
:返回数据库的产品名称。
getDatabaseProductVersion()
:返回数据库的版本号。
getDriverName()
:返回驱动驱动程序的名称。
getDriverVersion()
:返回驱动程序的版本号。
isReadOnly()
:返回一个boolean值,指示数据库是否只允许读操作。
工程(day16
)
例1.获取数据库的元信息(Demo4.java
)
package cn.itcast.demo;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import cn.itcast.utils.JdbcUtils_C3P0;
public class Demo4 {
public static void main(String[] args) throws SQLException {
Connection connection = JdbcUtils_C3P0.getConnection();
DatabaseMetaData metaData = connection.getMetaData();
System.out.println(metaData.getDatabaseProductName());
System.out.println(metaData.getDriverName());
}
}
二、元数据(ParameterMetaData)
PreparedStatement.getParameterMetaData()
获得代表PreparedStatement元数据的ParameterMetaData对象ParameterMetaData对象方法
getParameterCount()
获得指定参数的个数
getParameterType(int param)
获得指定参数的sql类型
例2.获取数据库的元信息(Demo5.java
)
package cn.itcast.demo;
import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import cn.itcast.utils.JdbcUtils_C3P0;
public class Demo5 {
public static void main(String[] args) throws SQLException {
Connection connection = JdbcUtils_C3P0.getConnection();
String sql = "select * from user where name = ? and password = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ParameterMetaData pm = ps.getParameterMetaData();
//得到参数的个数
System.out.println(pm.getParameterCount());
System.out.println(pm.getParameterType(1));
}
}
三、元数据(ResultSetMetaData)
ResultSet.getMetaData()
获得代表ResultSet对象元数据的ResultSetMetaData对象。ResultSetMetaData对象
getColumnCount()
返回resultset对象的列数
getColumnName(int column)
获得指定列的名称
getColumnTypeName(int column)
获得指定列的类型
例3.获取数据库元信息(Demo6.java
)
package cn.itcast.demo;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import cn.itcast.utils.JdbcUtils_C3P0;
public class Demo6 {
public static void main(String[] args) throws SQLException {
Connection connection = JdbcUtils_C3P0.getConnection();
String sql = "select * from account";
PreparedStatement pst = connection.prepareStatement(sql);
ResultSet result = pst.executeQuery();
ResultSetMetaData metadata = result.getMetaData();
System.out.println(metadata.getColumnCount());//有3列数据
System.out.println(metadata.getColumnName(1));
}
}
四、使用元数据简化JDBC代码
业务背景:系统中所有实体对象都涉及到基本的CRUD操作。所有实体的CUD操作代码基本相同,仅仅是发送给数据库的sql语句不同而已,因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的sql语句。
实体的R操作,除sql语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,因此可定义一个query方法,除以参数形式接收变化的sql语句外,可以使用策略模式由query方法的调用者决定如何把ResultSet中的数据映射到实体对象中。
这里我们通过编写一个自己的框架来进行说明(工程day16
):
我们先定义一个bean(使用的是day16这个数据库):
Account.java
package cn.itcast.domain;
public class Account {
private int id ;
private String name ;
private float money ;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getMoney() {
return money;
}
public void setMoney(float money) {
this.money = money;
}
}
对数据库工具类进行改进:
JdbcUtils.java
package cn.itcast.utils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class JdbcUtils {
private static ComboPooledDataSource ds = null;
static {
try {
ds = new ComboPooledDataSource();
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
public static void release(Connection conn, Statement ps , ResultSet result){
if(result != null){
try {
result.close();
} catch (Exception e) {
e.printStackTrace();
}
result = null;
}
if(ps != null){
try {
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
ps = null;
}
if(conn != null){
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
conn = null;
}
}
//除了sql语句和相关的参数不同外其他的代码都一样,这里可以替换我们之前在dao层中写的增删改方法
public static void update(String sql, Object[] params) throws SQLException{
Connection connection = null;
PreparedStatement ps = null;
ResultSet result = null ;
try{
connection = getConnection();
ps = connection.prepareStatement(sql);
for(int i = 0; i < params.length; i++){
ps.setObject(i + 1, params[i]);
}
ps.executeUpdate();
}finally{
release(connection, ps, result);
}
}
public static Object query(String sql, Object params[], ResultSetHandler rh) throws SQLException{
Connection conn = null ;
PreparedStatement ps = null ;
ResultSet result = null;
try{
conn = getConnection();
ps = conn.prepareStatement(sql);
for(int i = 0; i < params.length; i++){
ps.setObject(i + 1, params[i]);
}
result = ps.executeQuery();
return rh.handler(result);
}finally{
release(conn, ps, result);
}
}
}
说明:
- 1.对于增删改方法来说,可以将其相同部分抽取出来,其不同的地方只有sql语句和相关的参数值,这里我们让用户将这两个参数值传递进来即可。
- 2.对于查方法来说就有点不一样了,首先是最后执行的方法是executeQuery方法,其次是有返回值,当然对于前一个不同来说,我们只需要将executeUpdate换成executeQuery方法即可,但是对于结果集的处理来说就有点麻烦了。首先我们不知道返回的结果集是什么,当然这可以使用Object代替,但是我们不知道针对结果集如何处理,于是这里我们将结果集的处理方式交给用户。我们定义了一个接口让用户来实现:
ResultSetHandler.java
package cn.itcast.utils;
import java.sql.ResultSet;
public interface ResultSetHandler {
public Object handler(ResultSet result);
}
- 3.可以发现,让用户去写处理代码同样不太好,这样我们的框架就没有达到简化的效果,同时我们也看到一般用户查到相关的数据之后是会封装到一个javabean中去的,于是这里我们帮用户实现一个基本的处理方法:
BeanHandler.java
package cn.itcast.dao;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import cn.itcast.utils.ResultSetHandler;
public class BeanHandler implements ResultSetHandler {
private Class clazz;
public BeanHandler() {
}
public BeanHandler(Class clazz) {
this.clazz = clazz;
}
@Override
public Object handler(ResultSet result) {
//这里有个条件是表中字段名和bean中的属性名必须一致
try{
if(!result.next()){
return null;
}
//实例化对象
Object bean = clazz.newInstance();
ResultSetMetaData metaData = result.getMetaData();
int columnCount = metaData.getColumnCount();//得到结果集中数据列数
for(int i = 0; i < columnCount; i++){
//获得每一列的列名
String columnName = metaData.getColumnName(i + 1);
Object columnData = result.getObject(i + 1);
Field f = clazz.getDeclaredField(columnName);//反射出类上列名对应的属性
f.setAccessible(true);//暴力反射
f.set(bean, columnData);//在赋值的时候可以使用beanUtils框架工具
}
return bean;
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
说明:对于类的类型我们是不知道的,这里我们让用户在new的时候传递进来。然后我们就可以通过反射的方式将查询到的数据封装到用户传递进来的类的各个属性上去,但是这里实现的并不是很好,必须让表字段名和bean中的属性名一致。
- 4.当然很显然,有时候查出来的数据是一个集合,而不是像上面那种一个单一对象,于是我们又实现了针对集合的处理类:
BeanListHandler.java
package cn.itcast.dao;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
import cn.itcast.utils.ResultSetHandler;
public class BeanListHandler implements ResultSetHandler {
private Class clazz;
public BeanListHandler() {
}
public BeanListHandler(Class clazz) {
this.clazz = clazz;
}
@Override
public Object handler(ResultSet result) {
try {
List list = new ArrayList();
while(result.next()){
Object bean = clazz.newInstance();
ResultSetMetaData metaData = result.getMetaData();
int count = metaData.getColumnCount();
for(int i = 0; i < count; i++){
String name = metaData.getColumnName(i + 1);
Object value = result.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);
}
}
}
- 5.此时我们的增删改查工具类就可以简化为如下:
AccountDao.java
package cn.itcast.dao;
import java.sql.SQLException;
import java.util.List;
import cn.itcast.domain.Account;
import cn.itcast.utils.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 getAll() throws SQLException{
String sql = "select * from account";
Object params[] = {};
return (List)JdbcUtils.query(sql, params, new BeanListHandler(Account.class));
}
}
- 6.测试
Demo7.java
package cn.itcast.demo;
import java.sql.SQLException;
import java.util.List;
import cn.itcast.dao.AccountDao;
import cn.itcast.domain.Account;
public class Demo7 {
public static void main(String[] args) throws SQLException {
AccountDao dao = new AccountDao();
Account a = dao.find(1);
System.out.println(a.getName());
List list = dao.getAll();
System.out.println(list.size());
}
}
最后:这里编写一个JDBC框架的目的并不是要实现一个真正可用的框架,主要是加深对JDBC各类框架实现的原理的理解。