JDBC使用

感谢尚硅谷,感谢宋红康老师

视频地址:

尚硅谷JDBC核心技术视频教程(康师傅带你一站式搞定jdbc)_哔哩哔哩_bilibili

目录

一、JDBC介绍

1、数据的持久化

2.、JDBC的理解

3、图示理解

4、数据库的驱动

5、面向接口编程的思想

二、JDBC操作数据库

1、数据库的连接

2、JDBCUtils

3、Statement接口实现CRUD操作(了解)

Statement使用的弊端

4、PreparedStatement替换Statement实现CRUD操作

5、PreparedStatement操作Blob类型的变量

6、PreparedStatement实现高效的批量操作

7、PreparedStatement与Statement的异同

三、数据库的事务

1、事务事务处理的原则

2、代码实现

3、事务的属性

4、总结

四、Dao及其子类

五、数据库连接池

1、C3P0

2、DBCP

3、Druid

六、DBUtils提供的jar包实现CRUD操作


一、JDBC介绍

1、数据的持久化

持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。

2.、JDBC的理解

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API)

简单理解为:JDBC,是SUN提供的一套 API,使用这套API可以实现对具体数据库的操作(获取连接、关闭连接、DML、DDL、DCL)

3、图示理解

好处

  • 从开发程序员的角度:不需要关注具体的数据库的细节
  • 数据库厂商:只需要提供标准的具体实现。

4、数据库的驱动

数据库厂商针对于JDBC这套接口,提供的具体实现类的集合。

类似:

5、面向接口编程的思想

 JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。

不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。

二、JDBC操作数据库

1、数据库的连接

public class ConnectionTest {
    @Test
    public void getConnection5() throws Exception{

        //1.读取配置文件中的4个基本信息
        InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");

        Properties pros = new Properties();
        pros.load(is);

        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        String url = pros.getProperty("url");
        String driverClass = pros.getProperty("driverClass");

        //2.加载驱动
        Class.forName(driverClass);

        //3.获取连接
        Connection conn = DriverManager.getConnection(url, user, password);
        System.out.println(conn);


    }
}

jdbc.properties

user=root
password=12345
url=jdbc:mysql://localhost:3306/test
driverClass=com.mysql.jdbc.Driver

 此种方式的好处

  1. 实现了数据与代码的分离。实现了解耦。
  2. 如果需要修改配置文件信息,可以避免程序重新打包。

2、JDBCUtils

public class JDBCUtils {
    /**
     * 连接资源
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception{
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties.txt");

        Properties pros = new Properties();
        pros.load(is);

        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        String url = pros.getProperty("url");
        String driverClass = pros.getProperty("driverClass");

        //2.加载驱动
        Class.forName(driverClass);

        //3.获取连接
       Connection conn = DriverManager.getConnection(url, user, password);
        return conn;
    }


    /**
     * 关闭资源
     * @param conn
     * @param ps
     */
    //使用dbutils.jar中提供的Dbutils工具类实现资源的关闭
    public static void closeRessourse(Connection conn, Statement ps){
        try {
            if(ps!=null) {
                ps.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if(conn!=null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭资源
     * @param conn
     * @param ps
     * @param rs
     */
    public static void closeRessourse(Connection conn, Statement ps, ResultSet rs){
        try {
            if(ps!=null) {
                ps.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if(conn!=null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if(rs!=null) {
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

3、Statement接口实现CRUD操作(了解)

ResultSet

  • Object getObject(String columnLabel):根据列名获取对应数据表中的数据
  • ResultSetMetaData getMetaData():返回一个 ResultSetMetaData 对象,它包含查询结果集ResultSet对象的元数据(描述数据的数据,又称为中继数据),可以得到结果集中有哪些列,列名,列的别名等等。

ResultSetMetaData:

  • int getColumnCount():获取结果集的列数
  • String getColumnName(int column):获取列的名称
  • String getColumnLabel(int column):获取列的别名
public class User {

	private String user;
	private String password;
}
  /*
	 * 实现对数据表中数据的增删改操作
	 */
	public void update(String sql){
		Connection conn = null;
		Statement st = null;
		try {
			//1.获取数据库的连接
			conn = JDBCUtils.getConnection();
			
			//2.创建一个Statement的实例
			st = conn.createStatement();
			
			//3.根据提供的sql语句,执行
			st.execute(sql);
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			//4.资源的关闭
			JDBCUtils.close(conn, st);
		}
	}

/*
 * 实现对数据表的查询操作。需要使用结果集:ResultSet,
 * 返回表中的一条记录
 */
public <T> T get(String sql, Class<T> clazz) {
		T t = null;

		Connection conn = null;
		Statement st = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();

			st = conn.createStatement();

			rs = st.executeQuery(sql);

			// 获取结果集的元数据
			ResultSetMetaData rsmd = rs.getMetaData();

			// 获取结果集的列数
			int columnCount = rsmd.getColumnCount();

			if (rs.next()) {

				t = clazz.newInstance();

				for (int i = 0; i < columnCount; i++) {
					// //1. 获取列的名称
					// String columnName = rsmd.getColumnName(i+1);

					// 1. 获取列的别名
					// 注意:获取结果集中(相当于数据表)列的名称(别名)
					String columnName = rsmd.getColumnLabel(i + 1); 

					// 2. 根据列名获取对应数据表中的数据
					Object columnVal = rs.getObject(columnName);

					// 3. 将数据表中得到的数据,封装进对象
					// 注意:反射根据Java中类的属性获取Field对象
					Field field = clazz.getDeclaredField(columnName); 
					field.setAccessible(true);
					field.set(t, columnVal);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 关闭资源
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (st != null) {
				try {
					st.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}

			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}

		return null;
	}

Statement使用的弊端

 拼串
String sql = "insert into customers(name,email,birth)values('" + name +"','"+email+"','"+birth+"')";

SQL注入:
SELECT user,password FROM user_table WHERE user = '1'   AND password = '1 or 1 = 1' 

其他问题:

  • Statement没办法操作Blob类型变量
  • Statement实现批量插入时,效率较低 

4、PreparedStatement替换Statement实现CRUD操作

  • PreparedStatement 是Statement的子接口,可以解决Statement的sql注入问题,拼串问题。
  • PreparedStatement操作Blob的数据,而Statement做不到。 .
  • PreparedStatement可以实现更高效的批量操作。
    /**
	 * 通用的增删该操作
	 * @param sql
	 * @param args
	 * @return
	 */
	public void update(String sql,Object ...args){//sql中占位符的个数与可变形参的长度相同!
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			//1.获取数据库的连接
			conn = JDBCUtils.getConnection();
			//2.预编译sql语句,返回PreparedStatement的实例
			ps = conn.prepareStatement(sql);
			//3.填充占位符
			for(int i = 0;i < args.length;i++){
				ps.setObject(i + 1, args[i]);//小心参数声明错误!!
			}
			//4.执行
			ps.execute();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			//5.资源的关闭
			JDBCUtils.closeResource(conn, ps);
			
		}
		
		
	}

    /**
	 * 针对于不同的表的通用的查询操作,返回表中的一条记录
	 * @param clazz
	 * @param sql
	 * @param args
	 * @return
	 */
	public <T> T getInstance(Class<T> clazz,String sql, Object... args) {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();

			ps = conn.prepareStatement(sql);
			for (int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);
			}

			rs = ps.executeQuery();
			// 获取结果集的元数据 :ResultSetMetaData
			ResultSetMetaData rsmd = rs.getMetaData();
			// 通过ResultSetMetaData获取结果集中的列数
			int columnCount = rsmd.getColumnCount();

			if (rs.next()) {
				T t = clazz.newInstance();
				// 处理结果集一行数据中的每一个列
				for (int i = 0; i < columnCount; i++) {
					// 获取列值
					Object columValue = rs.getObject(i + 1);

					// 获取每个列的列名
					// String columnName = rsmd.getColumnName(i + 1);
					String columnLabel = rsmd.getColumnLabel(i + 1);

					// 给t对象指定的columnName属性,赋值为columValue:通过反射
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columValue);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps, rs);

		}

		return null;
	}

    /**
	 * 针对于不同的表的通用的查询操作,返回表中的多条记录
	 * @param clazz
	 * @param sql
	 * @param args
	 * @return
	 */
    public <T> List<T> getForList(Class<T> clazz,String sql, Object... args){
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();

			ps = conn.prepareStatement(sql);
			for (int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);
			}

			rs = ps.executeQuery();
			// 获取结果集的元数据 :ResultSetMetaData
			ResultSetMetaData rsmd = rs.getMetaData();
			// 通过ResultSetMetaData获取结果集中的列数
			int columnCount = rsmd.getColumnCount();
			//创建集合对象
			ArrayList<T> list = new ArrayList<T>();
			while (rs.next()) {
				T t = clazz.newInstance();
				// 处理结果集一行数据中的每一个列:给t对象指定的属性赋值
				for (int i = 0; i < columnCount; i++) {
					// 获取列值
					Object columValue = rs.getObject(i + 1);

					// 获取每个列的列名
					// String columnName = rsmd.getColumnName(i + 1);
					String columnLabel = rsmd.getColumnLabel(i + 1);

					// 给t对象指定的columnName属性,赋值为columValue:通过反射
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columValue);
				}
				list.add(t);
			}
			
			return list;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(conn, ps, rs);

		}

		return null;
	}

测试

    @Test
    public void test1() throws Exception {
        String sql="select id,name,email from customers where id < ?";
        List<Customer> list = getForList(Customer.class, sql, 12);
        list.forEach(System.out::println);
    }

    @Test
    public void test2(){
        String sql="delete from customers where id = ? ";
        update(sql,3);
    }

    @Test
    public void test3(){
        String sql="insert into customers(name,email,birth) values(?,?,?)";
        update(sql,"sss","sss","2021-03-10");
    }

    @Test
    public void test3(){
        String sql="update `order` set order_name = ? where order_id = ?";
        update(sql,"DD",2);
    }

5、PreparedStatement操作Blob类型的变量

BLOB (binary large object):二进制大对象,是一个可以存储二进制文件的容器。在计算机中,BLOB常常是数据库中用来存储二进制文件的字段类型。

BLOB是一个大文件,典型的BLOB是一张图片或一个声音文件,由于它们的尺寸,必须使用特殊的方式来处理(例如:上传、下载或者存放到一个数据库)。

向数据表customers中插入Blob类型的字段

	@Test
	public void testInsert() throws Exception{
		Connection conn = JDBCUtils.getConnection();
		String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)";
		
		PreparedStatement ps = conn.prepareStatement(sql);
		
		ps.setObject(1,"袁浩");
		ps.setObject(2, "yuan@qq.com");
		ps.setObject(3,"1992-09-08");
		FileInputStream is = new FileInputStream(new File("girl.jpg"));
		ps.setBlob(4, is);
		
		ps.execute();
		
		JDBCUtils.closeResource(conn, ps);
		
	}

查询数据表customers中Blob类型的字段,以文件的方式保存在本地

    @Test
    public void testQuery(){
        Connection conn = null;
        PreparedStatement ps = null;
        InputStream is = null;
        FileOutputStream fos = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            String sql = "select id,name,email,birth,photo from customers where id = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, 21);
            rs = ps.executeQuery();
            if(rs.next()){
                //			方式一:
                //			int id = rs.getInt(1);
                //			String name = rs.getString(2);
                //			String email = rs.getString(3);
                //			Date birth = rs.getDate(4);
                //方式二:
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");
                Date birth = rs.getDate("birth");

                Customer cust = new Customer(id, name, email, birth);
                System.out.println(cust);

                //将Blob类型的字段下载下来,以文件的方式保存在本地
                Blob photo = rs.getBlob("photo");
                is = photo.getBinaryStream();
                fos = new FileOutputStream("zhangyuhao.jpg");
                byte[] buffer = new byte[1024];
                int len;
                while((len = is.read(buffer)) != -1){
                    fos.write(buffer, 0, len);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{

            try {
                if(is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            JDBCUtils.closeRessourse(conn, ps, rs);
        }
    }

注意:

6、PreparedStatement实现高效的批量操作

层次一:使用Statement实现

    @Test
    public void testInsert1(){
        Connection conn = null;
        Statement st = null;
        try {
            conn = JDBCUtils.getConnection();
            st = conn.createStatement();
            long start=System.currentTimeMillis();
            for(int i=1;i<20000;i++) {
                String sql = "insert into goods(name)values('name_" + i + "')";
                st.execute(sql);
            }
            long end=System.currentTimeMillis();//13579
            System.out.println(end-start);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.closeRessourse(conn,st);
        }

    }

层次二:使用PreparedStatement替换Statement 

    @Test
    public void testInsert2() throws Exception {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            long start=System.currentTimeMillis();

            conn = JDBCUtils.getConnection();

            String sql="insert into goods(name) values(?)";

            ps = conn.prepareStatement(sql);

            for(int i=1;i<20000;i++){
                ps.setObject(1,"name_"+i);
                ps.execute();
            }

            long end=System.currentTimeMillis();//14447
            System.out.println(end-start);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeRessourse(conn,ps);
        }
    }

层次三

addBatch()、executeBatch()、clearBatch() 

mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理的支持。

?rewriteBatchedStatements=true

 写在配置文件的url后面

使用更新的mysql 驱动:mysql-connector-java-5.1.37-bin.jar

    @Test
    public void testInsert3() {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            long start=System.currentTimeMillis();
            conn = JDBCUtils.getConnection();
            String sql="insert into goods(name) values(?)";
            ps = conn.prepareStatement(sql);
            for(int i=1;i<20000;i++){
                ps.setObject(1,"name_"+i);

                //1.攒sql
                ps.addBatch();

                if(i%500==0){
                    //2.执行batch
                    ps.executeBatch();

                    //3.清空batch
                    ps.clearBatch();
                }
            }
            long end=System.currentTimeMillis();
            System.out.println(end-start);//401
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeRessourse(conn,ps);
        }
    }

层次四:设置连接不允许自动提交数据

比层次三多了一个这

  • conn.setAutoCommit(false);
    @Test
    public void testInsert4(){
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            long start=System.currentTimeMillis();
            conn = JDBCUtils.getConnection();

            //设置不允许自动提交数据
            conn.setAutoCommit(false);

            String sql="insert into goods(name) values(?)";
            ps = conn.prepareStatement(sql);
            for(int i=1;i<20000;i++){
                ps.setObject(1,"name_"+i);

                //1.赞sql
                ps.addBatch();
                if(i%500==0){
                    //2.执行batch
                    ps.executeBatch();

                    //3.清空batch
                    ps.clearBatch();
                }
            }
            //提交数据
            conn.commit();
            long end=System.currentTimeMillis();
            System.out.println(end-start);//338
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeRessourse(conn,ps);
        }
    }

7、PreparedStatement与Statement的异同

  1. 接口与子接口的关系
  2. 开发中,PreparedStatement替换Statement

三、数据库的事务

事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。

  • 一组逻辑操作单元:一个或多个DML操作

1、事务事务处理的原则

保证所事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。

说明:

1、数据一旦提交,就不可回滚

2、哪些操作会导致数据的自动提交?

 1)DML默认情况下,一旦执行,就会自动提交

  • set autocommit = false 对DDL操作失效

2)默认在关闭连接时,会自动的提交数据

2、代码实现

    @Test
	public void testUpdateWithTx() {
		Connection conn = null;
		try {
			conn = JDBCUtils.getConnection();
			System.out.println(conn.getAutoCommit());//true
			//1.取消数据的自动提交
			conn.setAutoCommit(false);
			
			String sql1 = "update user_table set balance = balance - 100 where user = ?";
			update(conn,sql1, "AA");
			
			//模拟网络异常
			System.out.println(10 / 0);
			
			String sql2 = "update user_table set balance = balance + 100 where user = ?";
			update(conn,sql2, "BB");
			
			System.out.println("转账成功");
			
			//2.提交数据
			conn.commit();
			
		} catch (Exception e) {
			e.printStackTrace();
			//3.回滚数据
			try {
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		}finally{
			//修改其为自动提交数据
			//主要针对于使用数据库连接池的使用
			try {
				conn.setAutoCommit(true);
			} catch (SQLException e) {
				e.printStackTrace();
			}
			
			JDBCUtils.closeResource(conn, null);
		}
		
	}

考虑到事务以后,实现的通用的增删改操作:

	public int update(Connection conn,String sql, Object... args) {
		PreparedStatement ps = null;
		try {
			// 1.预编译sql语句,返回PreparedStatement的实例
			ps = conn.prepareStatement(sql);
			// 2.填充占位符
			for (int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);// 小心参数声明错误!!
			}
			// 3.执行
			return ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 4.资源的关闭
			JDBCUtils.closeResource(null, ps);

		}
		return 0;

	}

考虑到事务以后,实现的通用的查询:

	public <T> T getInstance(Connection conn,Class<T> clazz,String sql, Object... args) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			
			ps = conn.prepareStatement(sql);
			for (int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);
			}

			rs = ps.executeQuery();
			// 获取结果集的元数据 :ResultSetMetaData
			ResultSetMetaData rsmd = rs.getMetaData();
			// 通过ResultSetMetaData获取结果集中的列数
			int columnCount = rsmd.getColumnCount();

			if (rs.next()) {
				T t = clazz.newInstance();
				// 处理结果集一行数据中的每一个列
				for (int i = 0; i < columnCount; i++) {
					// 获取列值
					Object columValue = rs.getObject(i + 1);

					// 获取每个列的列名
					// String columnName = rsmd.getColumnName(i + 1);
					String columnLabel = rsmd.getColumnLabel(i + 1);

					// 给t对象指定的columnName属性,赋值为columValue:通过反射
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columValue);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, ps, rs);

		}

		return null;
	}

3、事务的属性

数据操作过程中可能出现的问题:(针对隔离性)

数据库的四种隔离级别:(一致性和并发性:一致性越好,并发性越差)

查看并设置隔离级别:


 

4、总结

考虑到事务以后的数据库操作(重点)

1、获取数据库的连接

//方式1:手动获取连接 方式2:数据库连接池
Connection conn = JDBCUtils.getConnection(); 
//体现事务
conn.setAutoCommit(false);                  


2、如下的多个DML操作,作为一个事务出现:

  • 操作1:需要使用通用的增删改查操作
  • 操作2:需要使用通用的增删改查操作  
  • 操作3:需要使用通用的增删改查操作

通用的增删改查操作如何实现?

  • 手动使用PreparedStatement实现
  • 使用dbutils.jar中QueryRunner类
conn.commit();

3.如果出现异常,则:

conn.rollback();

4.关闭资源

//方式1:手动关闭资源  方式2:DbUtils类的关闭方法
JDBCUtils.closeResource(..,...,...);

四、Dao及其子类

封装了针对于数据表的通用的操作

public abstract class BaseDAO<T> {
	
	private Class<T> clazz = null;
	
	
	{	
		//获取当前BaseDAO的子类继承的父类中的泛型,this在这里指的是子类
		Type genericSuperclass = this.getClass().getGenericSuperclass();
		ParameterizedType paramType = (ParameterizedType) genericSuperclass;
		
		Type[] typeArguments = paramType.getActualTypeArguments();//获取了父类的泛型参数
		clazz = (Class<T>) typeArguments[0];//泛型的第一个参数
		
	}
	
	
	// 通用的增删改操作---version 2.0 (考虑上事务
	public int update(Connection conn, String sql, Object... args) {
		PreparedStatement ps = null;
		try {
			// 1.预编译sql语句,返回PreparedStatement的实例
			ps = conn.prepareStatement(sql);
			// 2.填充占位符
			for (int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);// 小心参数声明错误!!
			}
			// 3.执行
			return ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 4.资源的关闭
			JDBCUtils.closeResource(null, ps);

		}
		return 0;

	}

	// 通用的查询操作,用于返回数据表中的一条记录(version 2.0:考虑上事务
	public T getInstance(Connection conn, String sql, Object... args) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {

			ps = conn.prepareStatement(sql);
			for (int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);
			}

			rs = ps.executeQuery();
			// 获取结果集的元数据 :ResultSetMetaData
			ResultSetMetaData rsmd = rs.getMetaData();
			// 通过ResultSetMetaData获取结果集中的列数
			int columnCount = rsmd.getColumnCount();

			if (rs.next()) {
				T t = clazz.newInstance();
				// 处理结果集一行数据中的每一个列
				for (int i = 0; i < columnCount; i++) {
					// 获取列值
					Object columValue = rs.getObject(i + 1);

					// 获取每个列的列名
					// String columnName = rsmd.getColumnName(i + 1);
					String columnLabel = rsmd.getColumnLabel(i + 1);

					// 给t对象指定的columnName属性,赋值为columValue:通过反射
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columValue);
				}
				return t;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, ps, rs);

		}

		return null;
	}
	// 通用的查询操作,用于返回数据表中的多条记录构成的集合(version 2.0:考虑上事务
	public List<T> getForList(Connection conn, String sql, Object... args) {
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {

			ps = conn.prepareStatement(sql);
			for (int i = 0; i < args.length; i++) {
				ps.setObject(i + 1, args[i]);
			}

			rs = ps.executeQuery();
			// 获取结果集的元数据 :ResultSetMetaData
			ResultSetMetaData rsmd = rs.getMetaData();
			// 通过ResultSetMetaData获取结果集中的列数
			int columnCount = rsmd.getColumnCount();
			// 创建集合对象
			ArrayList<T> list = new ArrayList<T>();
			while (rs.next()) {
				T t = clazz.newInstance();
				// 处理结果集一行数据中的每一个列:给t对象指定的属性赋值
				for (int i = 0; i < columnCount; i++) {
					// 获取列值
					Object columValue = rs.getObject(i + 1);

					// 获取每个列的列名
					// String columnName = rsmd.getColumnName(i + 1);
					String columnLabel = rsmd.getColumnLabel(i + 1);

					// 给t对象指定的columnName属性,赋值为columValue:通过反射
					Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columValue);
				}
				list.add(t);
			}

			return list;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.closeResource(null, ps, rs);

		}

		return null;
	}

	//用于查询特殊值的通用的方法
	public <E> E getValue(Connection conn,String sql,Object...args){
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			ps = conn.prepareStatement(sql);
			for(int i = 0;i < args.length;i++){
				ps.setObject(i + 1, args[i]);
				
			}
			
			rs = ps.executeQuery();
			if(rs.next()){
				return (E) rs.getObject(1);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(null, ps, rs);
			
		}
		return null;
		
	}	
}

此接口用于规范针对于customers表的常用操作 

public interface CustomerDAO {

	/**
	 * 将cust对象添加到数据库中
	 * @param conn
	 * @param cust
	 */
	void insert(Connection conn,Customer cust);

	/**
	 * 
	 * 针对指定的id,删除表中的一条记录
	 * @param conn
	 * @param id
	 */
	void deleteById(Connection conn,int id);

	/**
	 * 
	 * 针对内存中的cust对象,去修改数据表中指定的记录
	 * @param conn
	 * @param cust
	 */
	void update(Connection conn,Customer cust);

	/**
	 * 
	 * 针对指定的id查询得到对应的Customer对象
	 * @param conn
	 * @param id
	 */
	Customer getCustomerById(Connection conn,int id);

	/**
	 * 查询表中的所记录构成的集合
	 * @param conn
	 * @return
	 */

	List<Customer> getAll(Connection conn);
	/**
	 * 
	 * 返回数据表中的数据的条目数
	 * @param conn
	 * @return
	 */

	Long getCount(Connection conn);
	
	/**
	 * 
	 * 返回数据表中最大的生日
	 * @param conn
	 * @return
	 */
	Date getMaxBirth(Connection conn);
	
}	
public class CustomerDAOImpl extends BaseDAO<Customer> implements CustomerDAO{
	

	@Override
	public void insert(Connection conn, Customer cust) {
		String sql = "insert into customers(name,email,birth)values(?,?,?)";
		update(conn, sql,cust.getName(),cust.getEmail(),cust.getBirth());
	}

	@Override
	public void deleteById(Connection conn, int id) {
		String sql = "delete from customers where id = ?";
		update(conn, sql, id);
	}

	@Override
	public void update(Connection conn, Customer cust) {
		String sql = "update customers set name = ?,email = ?,birth = ? where id = ?";
		update(conn, sql,cust.getName(),cust.getEmail(),cust.getBirth(),cust.getId());
	}

	@Override
	public Customer getCustomerById(Connection conn, int id) {
		String sql = "select id,name,email,birth from customers where id = ?";
		Customer customer = getInstance(conn, sql,id);
		return customer;
	}

	@Override
	public List<Customer> getAll(Connection conn) {
		String sql = "select id,name,email,birth from customers";
		List<Customer> list = getForList(conn, sql);
		return list;
	}

	@Override
	public Long getCount(Connection conn) {
		String sql = "select count(*) from customers";
		return getValue(conn, sql);
	}

	@Override
	public Date getMaxBirth(Connection conn) {
		String sql = "select max(birth) from customers";
		return getValue(conn, sql);
	}

}

五、数据库连接池

1、C3P0

导入jar包:

测试连接的代码

public class C3P0Test {
    //方式一
    @Test
    public void test1() throws PropertyVetoException, SQLException {
        ComboPooledDataSource cpds = new ComboPooledDataSource();
        cpds.setDriverClass( "com.mysql.jdbc.Driver" ); 
        cpds.setJdbcUrl("jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true" );
        cpds.setUser("root");
        cpds.setPassword("12345");
        //通过设置相关的参数,对数据库连接池进行管理
        //设置初始时数据库连接池中的连接数
        cpds.setInitialPoolSize(10);

        Connection conn = cpds.getConnection();
        System.out.println(conn);

        //销毁连接池
      //  DataSources.destroy(cpds);
    }
    //方式二:使用配置文件
    @Test
    public void test2() throws SQLException {

        ComboPooledDataSource cpds = new ComboPooledDataSource("hell3p0");
        Connection conn = cpds.getConnection();
        System.out.println(conn);
    }

}

 其中,配置文件定义在src下。名为:c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

    <named-config name="hell3p0">
        <!-- 提供获取连接的4个基本信息 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true</property>
        <property name="user">root</property>
        <property name="password">12345</property>

        <!-- 进行数据库连接池管理的基本信息 -->
        <!-- 当数据库连接池中的连接数不够时,c3p0一次性向数据库服务器申请的连接数 -->
        <property name="acquireIncrement">5</property>
        <!-- c3p0数据库连接池中初始化时的连接数 -->
        <property name="initialPoolSize">10</property>
        <!-- c3p0数据库连接池维护的最少连接数 -->
        <property name="minPoolSize">10</property>
        <!-- c3p0数据库连接池维护的最多的连接数 -->
        <property name="maxPoolSize">100</property>
        <!-- c3p0数据库连接池最多维护的Statement的个数 -->
        <property name="maxStatements">50</property>
        <!-- 每个连接中可以最多使用的Statement的个数 -->
        <property name="maxStatementsPerConnection">2</property>

    </named-config>
</c3p0-config>

2、DBCP

public class DBCPTest {
    //方式一:不推荐
    @Test
    public void testGetConnection() throws SQLException {
        //创建了DBCP的数据库连接池
        BasicDataSource source = new BasicDataSource();

        //设置基本信息
        source.setDriverClassName("com.mysql.jdbc.Driver");
        source.setUrl("jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true");
        source.setUsername("root");
        source.setPassword("12345");

        //还可以设置其他涉及数据库连接池管理的相关属性
        source.setInitialSize(10);
        source.setMaxActive(10);

       Connection conn = source.getConnection();
        System.out.println(conn);
    }

    //方式二:使用配置文件
    @Test
    public void testGetConnection1() throws Exception{
        Properties pros = new Properties();
        //方式一
        InputStream is =ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");
        //方式二
        //FileInputStream is = new FileInputStream(new File("src/dbcp.properties.txt"));
        pros.load(is);
        DataSource sourse = BasicDataSourceFactory.createDataSource(pros);
        Connection conn = sourse.getConnection();
        System.out.println(conn);
    }
}

 其中,配置文件定义在src下:dbcp.properties

url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
driverClassName=com.mysql.jdbc.Driver
username=root
password=12345

initialSize=10

3、Druid

public class DruidTest {
    @Test
    public void getConnection() throws Exception {
        Properties pros = new Properties();

        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
        pros.load(is);

        DataSource source = DruidDataSourceFactory.createDataSource(pros);
        Connection conn = source.getConnection();
        System.out.println(conn);
    }
}

其中,配置文件定义在src下:druid.properties 

url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
driverClassName=com.mysql.jdbc.Driver
username=root
password=12345

initialSize=10
maxActive=10

六、DBUtils提供的jar包实现CRUD操作

commons-dbutils是Apache组织提供的一个开源JDBC工具类库,封装了对于数据库的增删改查操作

使用现成的jar中的QueryRunner测试增、删、改的操作

public class QueryRunnerTest {
    //测试插入
	@Test
	public void testInsert() {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection();
			String sql = "insert into customers(name,email,birth)values(?,?,?)";
			int insertCount = runner.update(conn, sql, "小明","xiaoming@126.com","1997-09-08");
			System.out.println("添加了" + insertCount + "条记录");
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
		}
		
	}

	/*
     * 测试查询
	 * BeanHander:是ResultSetHandler接口的实现类,用于封装表中的一条记录。
	 */
	@Test
	public void testQuery1(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select id,name,email,birth from customers where id = ?";
			BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);
			Customer customer = runner.query(conn, sql, handler, 23);
			System.out.println(customer);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			JDBCUtls.closeResource(conn, null);	
		}	
	}
	
	/*
	 * BeanListHandler:是ResultSetHandler接口的实现类,用于封装表中的多条记录构成的集合。
	 */
	@Test
	public void testQuery2() {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select id,name,email,birth from customers where id < ?";
			
			BeanListHandler<Customer>  handler = new BeanListHandler<>(Customer.class);

			List<Customer> list = runner.query(conn, sql, handler, 23);
			list.forEach(System.out::println);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			
			JDBCUtils.closeResource(conn, null);
		}
	}
	
	/*
	 * MapHander:是ResultSetHandler接口的实现类,对应表中的一条记录。
	 * 将字段及相应字段的值作为map中的key和value
	 */
	@Test
	public void testQuery3(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select id,name,email,birth from customers where id = ?";
			MapHandler handler = new MapHandler();
			Map<String, Object> map = runner.query(conn, sql, handler, 23);
			System.out.println(map);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
		}
	}
	
	/*
	 * MapListHander:是ResultSetHandler接口的实现类,对应表中的多条记录。
	 * 将字段及相应字段的值作为map中的key和value。将这些map添加到List中
	 */
	@Test
	public void testQuery4(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select id,name,email,birth from customers where id < ?";
		
			MapListHandler handler = new MapListHandler();
			List<Map<String, Object>> list = runner.query(conn, sql, handler, 23);
			list.forEach(System.out::println);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
		}
	}
	/*
	 * ScalarHandler:用于查询特殊值
	 */
	@Test
	public void testQuery5(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			
			String sql = "select count(*) from customers";
			
			ScalarHandler handler = new ScalarHandler();
			
			Long count = (Long) runner.query(conn, sql, handler);
			System.out.println(count);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
		}
	}

	@Test
	public void testQuery6(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			
			String sql = "select max(birth) from customers";
			
			ScalarHandler handler = new ScalarHandler();
			Date maxBirth = (Date) runner.query(conn, sql, handler);
			System.out.println(maxBirth);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
			
		}
		
	}
	
	/*
	 * 自定义ResultSetHandler的实现类
	 */
	@Test
	public void testQuery7(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			
			String sql = "select id,name,email,birth from customers where id = ?";
			ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>(){

				@Override
				public Customer handle(ResultSet rs) throws SQLException {
//					System.out.println("handle");
//					return null;
					
//					return new Customer(12, "成龙", "Jacky@126.com", new Date(234324234324L));
					
					if(rs.next()){
						int id = rs.getInt("id");
						String name = rs.getString("name");
						String email = rs.getString("email");
						Date birth = rs.getDate("birth");
						Customer customer = new Customer(id, name, email, birth);
						return customer;
					}
					return null;
					
				}
				
			};
			Customer customer = runner.query(conn, sql, handler,23);
			System.out.println(customer);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
			
		}
		
	}

}

 使用dbutils.jar包中的DbUtils工具类实现连接等资源的关闭

    public static void closeResource1(Connection conn,Statement ps,ResultSet rs){
//		try {
//			DbUtils.close(conn);
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
//		try {
//			DbUtils.close(ps);
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
//		try {
//			DbUtils.close(rs);
//		} catch (SQLException e) {
//			e.printStackTrace();
//		}
		
		DbUtils.closeQuietly(conn);
		DbUtils.closeQuietly(ps);
		DbUtils.closeQuietly(rs);
	}

注意两个方法的区别 

    public static void closeQuietly(Connection conn) {
        try {
            close(conn);
        } catch (SQLException var2) {
        }

    }

    public static void close(Connection conn) throws SQLException {
        if (conn != null) {
            conn.close();
        }

    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鲁蛋儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值