JDBC框架

2020-01-21 09:38:29 浏览数 (1)

在实际的开发中,如果直接使用JDBC开发,是非常繁琐且麻烦的,所以现在的企业在开发web程序时,连接数据库一定会使用一些JDBC的框架。 在学习框架之前,得先掌握一些基础知识。

  • JDBC元数据(编写JDBC框架的基础)

首先就来学习一下JDBC元数据。 元数据就是数据库、表、列的定义信息。 元数据相关类(DataBaseMetaData),可以通过Connetion接口中的getMetaData()方法来获得一个DataBaseMetaData对象。 通过实例感受一下: 新建一个web项目,名为demo 因为在之前已经学习了数据库连接池技术,所以之后有关数据库操作的部分都可以使用连接池,推荐使用c3p0,因为它相较于别的连接池更加简单和人性化。 重新编写工具类JDBCUtils

代码语言:javascript复制
/**
 * JDBC 工具类,抽取公共方法
 * 
 * @author seawind
 * 
 */
public class JDBCUtils {
	//获得数据库的连接	通过c3p0连接池
	private static DataSource dataSource = new ComboPooledDataSource();
	
	public static Connection getConnection() throws SQLException{
		return dataSource.getConnection();
	}
	
	// 释放资源
	public static void release(ResultSet rs, Statement stmt, Connection conn) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}

		release(stmt, conn);
	}

	public static void release(Statement stmt, Connection conn) {
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			stmt = null;
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conn = null;
		}
	}
}

新建测试类MetaDataTest,编写测试代码

代码语言:javascript复制
@Test
public void demo1() throws SQLException{
	//通过Connection对象获得DataBaseMetaData
	Connection conn = JDBCUtils.getConnection();
	DatabaseMetaData databaseMetaData = conn.getMetaData();
	
	//通过DatabaseMetaData可以获得JDBC连接的参数信息
	System.out.println(databaseMetaData.getURL());
	System.out.println(databaseMetaData.getDriverName());
	System.out.println(databaseMetaData.getUserName());
	
	//获得某张表的主键信息
	ResultSet rs = databaseMetaData.getPrimaryKeys(null, null, "account");
	while(rs.next()){
		//获得表名
		System.out.println(rs.getString("TABLE_NAME"));
		//获得列名
		System.out.println(rs.getString(4));
	}
}

都是一些十分简单的API,没有什么可说的,自己看看API文档应该就能明白。

接下来是第二个内容,参数元数据(ParameterMataData),可以获得预编译sql语句中?的信息。 通过PreparedStatement调用getParameterMataData()得到。 演示一下。 编写测试代码

代码语言:javascript复制
@Test
public void demo2() throws SQLException{
	Connection conn = JDBCUtils.getConnection();
	String sql = "select * from account where name = ?";
	PreparedStatement stmt = conn.prepareStatement(sql);
	//通过ParameterMataData获得?的相关信息
	ParameterMetaData parameterMetaData = stmt.getParameterMetaData();
	//获得参数个数
	int count = parameterMetaData.getParameterCount();
	System.out.println(count);
	//获得参数类型	该方法并不是所有的数据库都支持	MySQL不支持
	int type = parameterMetaData.getParameterType(1);
	System.out.println(type);
}

其中的getParameterType()方法并不是所有的数据库都支持,而恰好MySQL数据就不支持该方法。那如何让该方法在MySQL数据库被支持呢?可以在配置Url的时候在后面加上?generateSimpleParameterMetadata=true,然后运行测试代码,该方法返回的值是12,通过查看源码,得知12代表的是数据库中的varchar类型。

代码语言:javascript复制
/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>VARCHAR</code>.
 */
  public final static int VARCHAR         =  12;

但是,请注意了,其实不管你的预编译sql语句的参数是什么类型,在MySQL数据库用该方法查询得到的数据类型永远都会是12,也就是varchar类型。

接下来是第三个元数据,叫做结果集元数据(ResultSetMetaData) 编写测试代码

代码语言:javascript复制
@Test
public void demo3() throws SQLException{
	//测试结果集元数据ResultSetMetaData
	Connection conn = JDBCUtils.getConnection();
	String sql = "select * from account";
	PreparedStatement stmt = conn.prepareStatement(sql);
	ResultSet rs = stmt.executeQuery();
	//获得结果集元数据
	ResultSetMetaData resultSetMetaData = rs.getMetaData();
	//获得列数
	int count = resultSetMetaData.getColumnCount();
	//打印数据表的第一行
	for(int i = 1;i <= count;i  ){
		System.out.print(resultSetMetaData.getColumnName(i)   "t");
	}
	System.out.println();
	
	//打印每列类型
	for(int i = 1;i <= count;i  ){
		System.out.print(resultSetMetaData.getColumnTypeName(i)   "t");
	}
	
	System.out.println();
	
	//打印表数据
	while(rs.next()){
		for(int i = 1;i <= count;i  ){
			System.out.print(rs.getObject(i)   "t");
		}
		System.out.println();
	}
}

有了元数据的基础之后,我们就可以自己来编写JDBC框架了。 先创建一个张表,并初始化数据

代码语言:javascript复制
create table account(
	id int primary key not null auto_increment,
	name varchar(40),
	money double
);

insert into account values(null,'aaa',1000);
insert into account values(null,'bbb',1000);
insert into account values(null,'ccc',1000);

现在就来为该表编写DAO程序。 新建com.wang.domain包,然后在该包下创建实体类Account。

代码语言:javascript复制
/**
 * 属性和列对应
 * @author Administrator
 *
 */
public class Account {
	private int id;
	private String name;
	private double 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 double getMoney() {
		return money;
	}

	public void setMoney(double money) {
		this.money = money;
	}
}

然后新建com.wang.dao包,在该包下创建类AccountDao。

代码语言:javascript复制
/**
 * 插入一个账户的数据
 * @param account
 */
public void insertAccount(Account account){
	Connection conn = null;
	PreparedStatement stmt = null;
	
	try {
		conn = JDBCUtils.getConnection();
		String sql = "insert into account values(null,?,?)";
		
		stmt = conn.prepareStatement(sql);
		//设置参数
		stmt.setString(1, account.getName());
		stmt.setDouble(2, account.getMoney());
		
		stmt.executeUpdate();
	} catch (SQLException e) {
		e.printStackTrace();
	}finally{
		JDBCUtils.release(stmt, conn);
	}
}

编写测试代码,调用执行

代码语言:javascript复制
public static void main(String[] args) {
		Account account = new Account();
		account.setName("ddd");
		account.setMoney(1000);
		new AccountDao().insertAccount(account);
}

查询数据库,数据被成功添加到数据库。

更新和删除的方法和插入类似,不作过多赘述。

假设这个时候有很多的数据库表,那我们都要给每一个数据库表编写相对应的方法,会发现,重复代码非常的多,怎么样能够简化它呢?我们应该抽取通用的方法代码。 新建包com.wang.framework,在该包下新建类JDBCFramework。

代码语言:javascript复制
/**
 * 自定义的JDBC框架
 * @author Administrator
 *
 */
public class JDBCFramework {
	
	/**
	 * 通用的insert,update和delete方法
	 * @param sql	预编译需要的sql
	 * @param args	根据sql中的?准备的参数
	 */
	public static void update(String sql,Object... args){
		Connection conn = null;
		PreparedStatement stmt = null;
		
		try {
			conn = JDBCUtils.getConnection();
			stmt = conn.prepareStatement(sql);
			
			//设置参数		根据?设置参数
			ParameterMetaData parameterMetaData = stmt.getParameterMetaData();
			//获得参数个数
			int count = parameterMetaData.getParameterCount();
			for(int i = 1;i <= count;i  ){
				stmt.setObject(i, args[i - 1]);
			}
			stmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.release(stmt, conn);
		}
	}
}

然后回到类AccountDao中,编写一个删除的方法。

代码语言:javascript复制
public void deleteAccount(Account account) {
		String sql = "delete from account where id = ?";
		JDBCFramework.update(sql, account.getId());
}

因为现在有了自己编写好的框架,所以实现一个删除方法是非常简单的。 编写测试代码

代码语言:javascript复制
public static void main(String[] args) {
	Account account = new Account();
	account.setId(3);
	new AccountDao().deleteAccount(account);
}

运行测试代码

id为3的用户数据被成功删除。 插入、修改方法和删除类似。但是该框架无法用于查询,因为没有查询所对应的结果集。 我们来抽取一个查询方法,用于数据库表的通用查询。 在com.wang.framework包下新建接口MyResultSetHandler。

代码语言:javascript复制
public interface MyResultSetHandler {
	
	/**
	 * 将结果集中的数据封装成对象
	 * @param rs
	 * @return
	 */
	public Object handle(ResultSet rs);
}

在JDBCFramework类中添加方法。

代码语言:javascript复制
/**
 * 通用的查询方法
 */
public static Object query(String sql,MyResultSetHandler handler,Object... args){
	Object obj = null;
	
	Connection conn = null;
	PreparedStatement stmt = null;
	ResultSet rs = null;
	
	try {
		conn = JDBCUtils.getConnection();
		stmt = conn.prepareStatement(sql);
		
		//设置参数
		ParameterMetaData parameterMetaData = stmt.getParameterMetaData();
		int count = parameterMetaData.getParameterCount();
		for(int i = 1;i <= count;i  ){
			stmt.setObject(i, args[i - 1]);
		}
		rs = stmt.executeQuery();
		obj = handler.handle(rs);
	} catch (SQLException e) {
		e.printStackTrace();
	}finally{
		JDBCUtils.release(rs, stmt, conn);
	}
	
	return obj;
}

这样查询框架就写好了,当调用者调用该方法时需要自己实现接口,完成查询。

代码语言:javascript复制
    public Account findById(int id){
		//使用自定义框架查询
		String sql = "select * from account where id = ?";
		MyResultSetHandler handler = new MyResultSetHandler() {
			
			public Object handle(ResultSet rs) {
				try {
					if(rs.next()){
						Account account = new Account();
						account.setId(rs.getInt("id"));
						account.setName(rs.getString("name"));
						account.setMoney(rs.getDouble("money"));
					}
				} catch (SQLException e) {
					e.printStackTrace();
				}
				return null;
			}
		};
		return (Account) JDBCFramework.query(sql, handler, id);
	}

编写测试代码。

代码语言:javascript复制
    public static void main(String[] args){
    	Account account = new AccountDao().findById(1);
		System.out.println(account.getName());
	}

运行测试代码,成功查询到表数据。

会发现,要想实现查询,调用者就必须实现接口,接下来我们利用泛型和反射帮助调用者实现接口。

这块内容有点复杂,就直接贴代码了。 修改MyResultSetHandler接口

代码语言:javascript复制
public interface MyResultSetHandler<T> {
	
	/**
	 * 将结果集中的数据封装成对象
	 * @param rs
	 * @return
	 */
	public T handle(ResultSet rs);
}

新建类MyBeanHandler

代码语言:javascript复制
    public class MyBeanHandler<T> implements MyResultSetHandler<T>{

	private Class<T> domainClass;

	public MyBeanHandler(Class<T> domainClass){
		this.domainClass = domainClass;
	}
	
	public T handle(ResultSet rs) {
		try{
			//获得结果集元数据
			ResultSetMetaData resultSetMetaData = rs.getMetaData();
			int count =resultSetMetaData.getColumnCount();
			
			BeanInfo beanInfo = Introspector.getBeanInfo(domainClass);
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			if(rs.next()){
				//获得实例
				T t = domainClass.newInstance()	;
				for(int i = 1;i <= count;i  ){
					String columnName = resultSetMetaData.getColumnName(i);
					//获得列名		需要去查找匹配属性
					for(PropertyDescriptor descriptor : propertyDescriptors){
						if(columnName.equals(descriptor.getName())){
							//列名存在一个同名的属性
							//将列的值存入属性
							Method method = descriptor.getWriteMethod();
							method.invoke(t,rs.getObject(columnName));
						}
					}
				}
				return t;
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}

修改类JDBCFramework的查询方法

代码语言:javascript复制
/**
 * 通用的查询方法
 */
	public static <T>T query(String sql,MyResultSetHandler<T> handler,Object... args){
		T obj = null;
		
		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rs = null;
		
		try {
			conn = JDBCUtils.getConnection();
			stmt = conn.prepareStatement(sql);
			
			//设置参数
			ParameterMetaData parameterMetaData = stmt.getParameterMetaData();
			int count = parameterMetaData.getParameterCount();
			for(int i = 1;i <= count;i  ){
				stmt.setObject(i, args[i - 1]);
			}
			rs = stmt.executeQuery();
			obj = handler.handle(rs);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.release(rs, stmt, conn);
		}
		
		return obj;
	}

在AccountD类中修改查询方法

代码语言:javascript复制
public Account findById(int id){
		String sql = "select * from account where id = ?";
		return JDBCFramework.query(sql, new MyBeanHandler<Account>(Account.class),id);
}

重新运行测试代码,查询成功。

0 人点赞