• JDBC框架


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

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

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

    /**
     * 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,编写测试代码

    @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()得到。
    演示一下。
    编写测试代码

    @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类型。

    /**
     * <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)
    编写测试代码

    @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) + "	");
    	}
    	System.out.println();
    	
    	//打印每列类型
    	for(int i = 1;i <= count;i++){
    		System.out.print(resultSetMetaData.getColumnTypeName(i) + "	");
    	}
    	
    	System.out.println();
    	
    	//打印表数据
    	while(rs.next()){
    		for(int i = 1;i <= count;i++){
    			System.out.print(rs.getObject(i) + "	");
    		}
    		System.out.println();
    	}
    }
    

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

    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。

    
    /**
     * 属性和列对应
     * @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。

    /**
     * 插入一个账户的数据
     * @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);
    	}
    }
    

    编写测试代码,调用执行

    public static void main(String[] args) {
    		Account account = new Account();
    		account.setName("ddd");
    		account.setMoney(1000);
    		new AccountDao().insertAccount(account);
    }
    

    查询数据库,数据被成功添加到数据库。
    在这里插入图片描述
    更新和删除的方法和插入类似,不作过多赘述。

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

    
    /**
     * 自定义的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中,编写一个删除的方法。

    public void deleteAccount(Account account) {
    		String sql = "delete from account where id = ?";
    		JDBCFramework.update(sql, account.getId());
    }
    

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

    public static void main(String[] args) {
    	Account account = new Account();
    	account.setId(3);
    	new AccountDao().deleteAccount(account);
    }
    

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

    public interface MyResultSetHandler {
    	
    	/**
    	 * 将结果集中的数据封装成对象
    	 * @param rs
    	 * @return
    	 */
    	public Object handle(ResultSet rs);
    }
    
    

    在JDBCFramework类中添加方法。

    /**
     * 通用的查询方法
     */
    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;
    }
    

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

    
        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);
    	}
    
    

    编写测试代码。

        public static void main(String[] args){
        	Account account = new AccountDao().findById(1);
    		System.out.println(account.getName());
    	}
    

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

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

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

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

    新建类MyBeanHandler

    
        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的查询方法

    
    /**
     * 通用的查询方法
     */
    	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类中修改查询方法

    public Account findById(int id){
    		String sql = "select * from account where id = ?";
    		return JDBCFramework.query(sql, new MyBeanHandler<Account>(Account.class),id);
    }
    

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

  • 相关阅读:
    控制台日志输入到文件指定文件中
    flutter环境搭建坑一
    hybridapp/webapp的前端UI框架推荐
    hybrid app、web app与native app工具
    浏览记录
    HTML5跨平台APP越来越火,工具也越来越多。我推荐一个开发平台(一款工具)----DCloud
    学个p-nan isnan . isna isnull
    学个p-np.triu(m, k),np.tirl()
    实验五 plsql基础
    实验四-数据插入、更新、删除
  • 原文地址:https://www.cnblogs.com/blizzawang/p/11411594.html
Copyright © 2020-2023  润新知