前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JDBC框架

JDBC框架

作者头像
wangweijun
发布2020-01-21 09:38:29
4280
发布2020-01-21 09:38:29
举报
文章被收录于专栏:wangweijunwangweijun

在实际的开发中,如果直接使用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);
}

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档