学习资源
学习目录
学习笔记
一、概述
- 特点
- 独立于特定数据库管理系统,通用的SQL数据库存取和操作的公共接口。
- 标准的方法方便访问数据库
- 不同数据库提供了统一途径
- 存储技术分类
- JDBC (基石)
- JDO
- 第三方 O/R 工具,如 Hibernate、Mybatis
- 编写步骤
- 导入java.sql包
- JDBC-ODBC桥方式(SQL SERVER) / 附加相应厂商提供的驱动(Oracle、MySQL)
- 加载并注册驱动程序
- 创建 Connection 对象
- 创建 Statement对象
- 执行SQL语句
- 使用 ResultSet 对象
- 关闭 ResultSet 对象
- 关闭 Statement 对象
- 关闭 Connection 对象
二、数据库连接
url
- 语法:
jdbc:mysql://主机IP:数据库端口号/数据库名 - jdbc:mysql 是协议
数据库名区分大小写 ```java // 方式一: @Test public void testConnection1() throws SQLException { Driver driver = new com.mysql.jdbc.Driver();
// url String url = “jdbc:mysql://...:/“;
// 用户名和密码 Properties properties = new Properties(); properties.setProperty(“user”, “*“); // 设置用户名 properties.setProperty(“password”, “**“); // 设置密码
Connection connect = driver.connect(url, properties);
System.out.println(connect); // 不报错就代表连接成功了 }
- 语法:
/**
* 对方式一的迭代,程序中不出现第三方的API,是程序具有更好的移植性* @throws Exception*/
@Test public void testConnection2() throws Exception { // 1. 获取Driver实现类对象,使用反射 Class clazz = Class.forName(“com.mysql.jdbc.Driver”); Driver driver = (Driver) clazz.newInstance();
// 2. 提供要链接的数据库String url = "jdbc:mysql://********:********/********";// 3. 用户名和密码Properties properties = new Properties();properties.setProperty("user", "********");properties.setProperty("password", "********");// 4. 获取连接Connection connect = driver.connect(url, properties);System.out.println(connect);
}
/**
* 方式三:使用DriverManager替换Driver*/
@Test public void testConnection3 () throws Exception { // 1. 获取Driver实现类对象,使用反射 Class clazz = Class.forName(“com.mysql.jdbc.Driver”); Driver driver = (Driver) clazz.newInstance();
// 2. 提供要链接的数据库String url = "jdbc:mysql://********/*****";String user = "********";String psd = "********";// 注册驱动DriverManager.registerDriver(driver);// 获取连接Connection connection = DriverManager.getConnection(url, user, psd);System.out.println(connection);
}
/**
* 方式四:优化方式三,只需加载驱动,不需要再注册驱动了*/
@Test
public void testConnection4 () throws Exception {
// 1. 提供要链接的数据库
String url = “jdbc:mysql://**/“;
String user = “**“;
String psd = “**“;
// 2. 获取Driver实现类对象,使用反射// 相比方式三,省略了注册驱动的操作,mysql自动注册了驱动// 其实这行也可以注释掉一样可以成功连接数据库,只不过这样不安全,所以不能这样使用// 原因是jar包中缓存了第三方API名Class.forName("com.mysql.jdbc.Driver");// 3. 获取连接Connection connection = DriverManager.getConnection(url, user, psd);System.out.println(connection);
}
/**
* 方式五(final版):将数据库连接所需的基本信息声明在配置文件中* 暴露的内容少* 解耦,配置和代码解耦* 如果修改配置文件信息,避免程序重新打包*/
@Test public void testConnection5() throws Exception { // 1. 读取配置文件中基本信息 InputStream resourceAsStream = ConnectionTest.class.getClassLoader().getResourceAsStream(“jdbc/jdbc.properties”);
Properties properties = new Properties();properties.load(resourceAsStream);String user = properties.getProperty("user");String password = properties.getProperty("password");String url = properties.getProperty("url");String driverClass = properties.getProperty("driverClass");// 2. 加载驱动Class.forName(driverClass);// 3. 获取连接Connection conn = DriverManager.getConnection(url, user, password);System.out.println(conn);
}
<a name="PtvBY"></a>## 三、PreparedStatement实现CRUD操作- 一个数据库连接就是一个Socket连接- java.sql 有以下三个接口- Statement (已经不使用了 )- 无法防止SQL恶意注入问题,避免方式就是转而使用 PreparetdStatement- **PrepatedStatement** 主要使用(是Statement的子接口)- CallableStatement 用于执行SQ L存储过程- 表名与类名不想同时- 必须声明sql时,使用类的属性名来命名字段的别名- 使用 ResultMetaData 时,需要使用 getColumnLabel() 来替换 getColumnName(),获取列的别名。没起别名 getColumnLabel 获取的就是表中的列名- ORM编程思想- 一个数据**表**对应一个**类**- 一条**记录**对应一个**对象**- 一个**字段**对应一个**属性****- 执行代码区别- `prepareStatement.`**`execute`**`()` 返回true / false, 代表是否查询成功- `prepareStatement.`**`executeUpdate`**`()` 返回数字类型,代表update 影响了多少行记录- `prepareStatement.`**`executeQuery`**`()`**- 代码- 【封装】数据连接与关闭```java/*** [封装] 数据库连接函数* 1. 读取配置 properties* 2. 加载驱动 Class.forName* 3. 获取连接 DriverManager.getConnection* @return Connection 数据库连接对象实例* @throws Exception*/public static Connection getConnection() throws Exception {// 1. 读取配置InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc/jdbc.properties");Properties pro = new Properties();pro.load(resourceAsStream);String user = pro.getProperty("user");String password = pro.getProperty("password");String url = pro.getProperty("url");String driverClass = pro.getProperty("driverClass");// 2. 加载驱动Class.forName(driverClass);// 3. 获取连接Connection conn = DriverManager.getConnection(url, user, password);return conn;}/*** [封装] 关闭连接和Statement* @param conn 数据库连接* @param preparedStatement 这里使用了父接口声明*/public static void closeResource(Connection conn, Statement preparedStatement) {// 7. 资源关闭if (preparedStatement != null) {try {preparedStatement.close();} catch (SQLException throwables) {throwables.printStackTrace();}}if (conn != null) {try {conn.close();} catch (SQLException throwables) {throwables.printStackTrace();}}}/*** [封装] 关闭连接和Statement 还有resultSet* @param conn 数据库连接* @param preparedStatement 这里使用了父接口声明*/public static void closeResource(Connection conn, Statement preparedStatement, ResultSet resultSet) {// 7. 资源关闭closeResource(conn, preparedStatement);if (resultSet != null) {try {resultSet.close();} catch (SQLException throwables) {throwables.printStackTrace();}}}
- 增 **删 **改
``sql @Test public void testOtherTable() { // 这里因为order是关键字,所以需要加单引号区分 String sql = "updateorder` set order_name=? where order_id=?”; commonSql(sql, “DD”, 2); }
/**
- 通用的增删改操作
占位符个数应该和可变形参长度相同 */ public void commonSql(String sql, Object …args) { Connection connection = null; PreparedStatement ps = null; try {
// 1. 获取数据库连接connection = JDBCUtils.getConnection();// 2. 预编译sql语句ps = connection.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(connection, ps);
} } ```
查 ```sql public Order orderForQuery(String sql, Object …args) { Connection conn = null; PreparedStatement ps = null; ResultSet resultSet = null; try {
// 1. 连接数据库
conn = JDBCUtils.getConnection(); // 2. 预编译sql语句 ps = conn.prepareStatement(sql);
// 3. 填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1, args[i]);
} // 4. 获取结果集 resultSet = ps.executeQuery(); // 5. 获取元数据 ResultSetMetaData metaData = resultSet.getMetaData(); // 6. 获取总列数 int columnCount = metaData.getColumnCount();
if (resultSet.next()) {
Order order = new Order();for (int i = 0; i < columnCount; i++) {// 7. 获取结果集中的属性Object object = resultSet.getObject(i + 1);// 8. 获取列的别名(如果列没有设置别名时使用数据库表中的名字)String columnName = metaData.getColumnLabel(i + 1);// 9. 利用反射获取属性Field declaredField = Order.class.getDeclaredField(columnName);declaredField.setAccessible(true); // 避免字段private导致报错declaredField.set(order, object);}return order;
} } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.closeResource(conn, ps, resultSet); }
return null; }
@Test
public void testOrderForQuery() {
String sql = “select order_id orderId, order_name orderName, order_date orderDate from order where order_id=?”;
Order order = orderForQuery(sql, 4);
System.out.println(order);
}
- **查 2.0 (使用泛型,不再固定使用某一个类)**```sql/*** 通过泛型,不再固定使用某一个类* @param clazz* @param sql* @param args* @param <T>* @return*/public <T> T getInstance(Class<T> clazz, String sql, Object ...args) {Connection conn = null;PreparedStatement ps = null;ResultSet resultSet = null;try {// 1. 连接数据库conn = JDBCUtils.getConnection();// 2. 预编译sql语句ps = conn.prepareStatement(sql);// 3. 配置占位符for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);}// 4. 结果集resultSet = ps.executeQuery();// 5. 结果集的元数据ResultSetMetaData metaData = resultSet.getMetaData();// 6. 通过元数据获取结果集中的列数int columnCount = metaData.getColumnCount();if (resultSet.next()) {// 7. 更改为使用泛型T t = clazz.newInstance();for (int i = 0; i < columnCount; i++) {// 8. 获取结果集中的属性Object object = resultSet.getObject(i + 1);// 9. 获取每个列的别名String columnName = metaData.getColumnLabel(i + 1);// 10. 给泛型对象指定的columnName属性,赋值为columnValue,通过反射Field declaredField = clazz.getDeclaredField(columnName);declaredField.setAccessible(true); // 给予权限,避免private报错declaredField.set(t, object);}return t;}} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, ps, resultSet);}return null;}@Testpublic void testGetInstance() {String sql = "select id,name,email from customers where id = ?";Customer instance = getInstance(Customer.class, sql, 12);System.out.println(instance);String sql1 = "select order_id orderId, order_name orderName from `order` where order_id = ?";Order instance1 = getInstance(Order.class, sql1, 2);System.out.println(instance1);}
查 3.0 (查询多条数据) ```sql public
List getForList(Class clazz, String sql, Object… args) { Connection conn = null; PreparedStatement ps = null; ResultSet resultSet = null; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
resultSet = ps.executeQuery(); // 获取结果集的元数据 ResultSetMetaData metaData = resultSet.getMetaData(); // 通过元数据获取结果集中的列数 int columnCount = metaData.getColumnCount();
// 创建集合对象 ArrayList
list = new ArrayList (); // 修改为while,这样看来,每一次next都是切换一条数据 while (resultSet.next()) { // 更改为使用泛型T t = clazz.newInstance();for (int i = 0; i < columnCount; i++) {Object object = resultSet.getObject(i + 1);// 获取每个列的列名String columnName = metaData.getColumnLabel(i + 1);// 给泛型对象指定的columnName属性,赋值为columnValue,通过反射Field declaredField = clazz.getDeclaredField(columnName);declaredField.setAccessible(true); // 给予权限,避免private报错declaredField.set(t, object);}// 向集合中添加元素list.add(t);
}
return list; } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.closeResource(conn, ps, resultSet); } return null; }
@Test
public void testQueryList() {
String sql = “select id, name, email from customers where id < ?”;
List
<a name="fMBEi"></a>## 四、操作BLOB类型字段- BLOB类型分类:- TinyBlob 255- Blob 65K- MediumBlob 16M- LongBlob 4g<a name="QK0HW"></a>## 五、批量插入- PreparedStatement 和 Statement 对比- 提高可读性和可维护性- PreparedStatement可防止SQL注入- PreparedStatement可以批处理,预编译- 代码```sql@Testpublic void testInsert1() {Connection conn = null;PreparedStatement ps = null;try {long start = System.currentTimeMillis();conn = JDBCUtils.getConnection();ps = conn.prepareStatement("insert into goods(name) values (?)");for (int i = 0; i < 20000; i++) {ps.setObject(1, "name_" + i);ps.execute();}long end = System.currentTimeMillis();System.out.println("花费的时间为" + (end - start));} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, ps);}}/*** 批量插入方式三:* 1. addBatch()、executeBatch()、clearBatch()* 2. mysql默认情况下关闭批处理batch的,* 需要通过一个参数,来获得mysql批处理支持* 这个参数添加在 url 后面, ?rewriteBatchedStatements=true* 3. 使用更新的mysql驱动: mysql-connector-java-5.1.37-bin.jar*/@Testpublic void testInsert2() {Connection conn = null;PreparedStatement ps = null;try {long start = System.currentTimeMillis();conn = JDBCUtils.getConnection();ps = conn.prepareStatement("insert into goods(name) values (?)");for (int i = 0; i < 1000000; i++) {ps.setObject(1, "name_" + i);// 1. “攒”sqlps.addBatch();if (i % 1000 == 0) {// 2. 执行batchps.executeBatch();// 3. 清空batchps.clearBatch();}}long end = System.currentTimeMillis();System.out.println("花费的时间为" + (end - start));} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, ps);}}/*** 批量插入方式四:* 设置不允许自动提交,待全部完成再提交*/@Testpublic void testInsert3() {Connection conn = null;PreparedStatement ps = null;try {long start = System.currentTimeMillis();conn = JDBCUtils.getConnection();// 设置不允许自动提交数据conn.setAutoCommit(false);ps = conn.prepareStatement("insert into goods(name) values (?)");for (int i = 0; i < 1000000; i++) {ps.setObject(1, "name_" + i);// 1. “攒”sqlps.addBatch();if (i % 1000 == 0) {// 2. 执行batchps.executeBatch();// 3. 清空batchps.clearBatch();}}// 提交树conn.commit();long end = System.currentTimeMillis();System.out.println("花费的时间为" + (end - start));} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, ps);}}
六、数据库事务
- 自动提交的情况
- DDL 操作一旦执行,就会自动提交
- set autocommit false 不起作用
- DML 默认情况下,一旦执行就会默认提交
- set autocommit false 管用
- 默认在关闭连接时,自动提交数据
- DDL 操作一旦执行,就会自动提交
七、DAO及相关实现类
- BaseDao
通用的基础父类,其他每个表都可以继承此表获得封装好的方法
package jdbc.dao.daoPro;import jdbc.util.JDBCUtils;import java.lang.reflect.Field;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.sql.*;import java.util.ArrayList;import java.util.List;/*** [进阶版] 进一步省去传入 Customer.class 操作,获取父类的类型,泛型* 封装了针对数据库的通用操作*/public class BaseDao <T> {private Class<T> clazz = null;/*** 在父类中对 clazz 初始化* 避免每一个继承的子类都要给其初始化*/{// 当前对象的父类的泛型,new 谁,这个this就指代 谁Type genericSuperclass = this.getClass().getGenericSuperclass();// 强转为带参泛型ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;// 获取了父类的泛型参数,// 因为泛型可能有多个,所以这里返回是数组// 但是实际上我们知道只传递了一个参数,所以数组第一个参数就是我们要的参数Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();// 泛型的第一个i参数,这里仍然需要一个强转clazz = (Class<T>)actualTypeArguments[0];}/*** 查询* 通过泛型,不再固定使用某一个类* version2, 考虑加入事务的情况* @param conn* @param sql* @param args* @param <T>* @return*/public T getInstance(Connection conn, String sql, Object... args) {PreparedStatement ps = null;ResultSet resultSet = null;try {ps = conn.prepareStatement(sql);for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);}resultSet = ps.executeQuery();// 获取结果集的元数据ResultSetMetaData metaData = resultSet.getMetaData();// 通过元数据获取结果集中的列数int columnCount = metaData.getColumnCount();if (resultSet.next()) {// 更改为使用泛型T t = clazz.newInstance();for (int i = 0; i < columnCount; i++) {Object object = resultSet.getObject(i + 1);// 获取每个列的列名String columnName = metaData.getColumnLabel(i + 1);// 给泛型对象指定的columnName属性,赋值为columnValue,通过反射Field declaredField = clazz.getDeclaredField(columnName);declaredField.setAccessible(true); // 给予权限,避免private报错declaredField.set(t, object);}return t;}} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(null, ps, resultSet);}return null;}/*** 通用的查询操作,返回多条记录的集合* @param conn* @param sql* @param args* @param <T>* @return*/public List<T> getForList(Connection conn, String sql, Object... args) {PreparedStatement ps = null;ResultSet resultSet = null;try {ps = conn.prepareStatement(sql);for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);}resultSet = ps.executeQuery();// 获取结果集的元数据ResultSetMetaData metaData = resultSet.getMetaData();// 通过元数据获取结果集中的列数int columnCount = metaData.getColumnCount();// 创建集合对象ArrayList<T> list = new ArrayList<T>();while (resultSet.next()) {// 更改为使用泛型T t = clazz.newInstance();for (int i = 0; i < columnCount; i++) {Object object = resultSet.getObject(i + 1);// 获取每个列的列名String columnName = metaData.getColumnLabel(i + 1);// 给泛型对象指定的columnName属性,赋值为columnValue,通过反射Field declaredField = clazz.getDeclaredField(columnName);declaredField.setAccessible(true); // 给予权限,避免private报错declaredField.set(t, object);}// 向集合中添加元素list.add(t);}return list;} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(null, ps, resultSet);}return null;}/*** 通用增删改操作* @param conn* @param sql* @param args* @return*/public int update(Connection conn, String sql, Object... args) {PreparedStatement ps = null;try {// 1. 预编译sql语句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;}/*** 用于查询特殊值的通用方法* @param conn* @param sql* @param args* @param <E>* @return*/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 throwables) {throwables.printStackTrace();} finally {JDBCUtils.closeResource(null, ps, rs);}return null;}}
- CustomerDAO
Customer 接口,针对每一个表创建一个此接口以规范操作 customers 表的方法
package jdbc.dao.daoPro;import jdbc.bean.Customer;import java.sql.Connection;import java.sql.Date;import java.util.List;/*** 此接口用于规范对于customerd表的常用操作*/public interface CustomerDAO {/*** 将 customer 对象添加到数据库* @param conn* @param customer*/void insert(Connection conn, Customer customer);/*** 删除表中指定id的数据* @param conn* @param id*/void deleteById(Connection conn, int id);/*** 更新数据表中customer对象的数据* @param conn* @param customer*/void update(Connection conn, Customer customer);/*** 通过id获得customer对象* @param conn* @param id* @return*/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);}
- CustomerDAOImpl
上个接口的实现类,继承了 BaseDao 并实现了 CustomerDAO
package jdbc.dao.daoPro;import jdbc.bean.Customer;import java.sql.Connection;import java.sql.Date;import java.util.List;public class CustomerDAOImpl extends BaseDao <Customer> implements CustomerDAO {// 获取当前类的父类的泛型,即获取 <Customer>@Overridepublic void insert(Connection conn, Customer customer) {String sql = "insert into customers(name, email, birth) values(?, ?, ?)";update(conn, sql, customer.getName(), customer.getEmail(), customer.getBirth());}@Overridepublic void deleteById(Connection conn, int id) {String sql = "delete from customers where id = ?";update(conn, sql, id);}@Overridepublic void update(Connection conn, Customer customer) {String sql = "update customers set name=?, email=?, birth=? where id = ?";update(conn, sql, customer.getName(), customer.getEmail(), customer.getBirth(), customer.getId());}@Overridepublic Customer getCustomerById(Connection conn, int id) {/*** 为什么不使用 * 代替?* 因为如果使用 * 的话会带出 photo 属性,这个属性目前是反射没办法处理的,所以就选择指定的属性返回。*/String sql = "select id, name, email, birth from customers where id = ?";return getInstance(conn, sql, id);}@Overridepublic List<Customer> getAll(Connection conn) {String sql = "select id, name, email, birth from customers";return getForList(conn, sql);}@Overridepublic Long getCount(Connection conn) {// 因为返回的long类型,所以不需要做转换了,泛型return getValue(conn, "select count(*) from customers");}@Overridepublic Date getMaxBirth(Connection conn) {// 因为返回Date类型,所以不需要做转换了,泛型return getValue(conn, "select max(birth) from customers");}}
- Test
测试类,具体使用细节在这里
package jdbc.dao.daoPro.junit;import jdbc.bean.Customer;import jdbc.util.JDBCUtils;import jdbc.dao.daoPro.CustomerDAOImpl;import org.junit.Test;import java.sql.Connection;import java.sql.Date;import java.text.SimpleDateFormat;import java.util.List;public class CustomerDAOImplTest {private CustomerDAOImpl dao = new CustomerDAOImpl();@Testpublic void insert() {Connection conn = null;try {conn = JDBCUtils.getConnection();SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd" );Long timestamp = sdf.parse("2020-10-26").getTime();// 这里写id其实没什么用,因为是自增idCustomer customer = new Customer(1, "天下", "tianxia@163.com", new Date(timestamp));dao.insert(conn, customer);System.out.println("添加成功");} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}@Testpublic void deleteById() {Connection conn = null;try {conn = JDBCUtils.getConnection();int id = 24;dao.deleteById(conn, id);System.out.println(id + " 删除成功");} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}@Testpublic void update() {Connection conn = null;try {conn = JDBCUtils.getConnection();SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd" );Long timestamp = sdf.parse("2020-10-26").getTime();// 这里写id其实没什么用,因为是自增idCustomer customer = new Customer(18, "贝少芬", null, new Date(timestamp));dao.update(conn, customer);System.out.println(customer.getId() + " 修改成功");} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}@Testpublic void getCustomerById() {Connection conn = null;try {conn = JDBCUtils.getConnection();Customer customerById = dao.getCustomerById(conn, 18);System.out.println(customerById);} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}@Testpublic void getAll() {Connection conn = null;try {conn = JDBCUtils.getConnection();List<Customer> list = dao.getAll(conn);list.forEach(System.out::println);} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}@Testpublic void getCount() {Connection conn = null;try {conn = JDBCUtils.getConnection();Long count = dao.getCount(conn);System.out.println(count);} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}@Testpublic void getMaxBirth() {Connection conn = null;try {conn = JDBCUtils.getConnection();Date maxBirth = dao.getMaxBirth(conn);System.out.println(maxBirth);} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}}
八、数据库连接池
- 常用连接池集数:
- DBCP
- 速度比c3p0快,但是存在bug,不再提供支持
- C3P0
- 速度较慢,稳定性强,hibernate推荐
- Proxool
- BoneCP
- Druid
- 阿里提供
- DBCP
- 代码 ```sql package jdbc.connection.util;
import com.alibaba.druid.pool.DruidDataSourceFactory; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.sql.Connection; import java.util.Properties;
public class JDBCUtils { /**
* 避免每次新new对象,都创建一份新的连接池*/private static ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");/*** 使用 c3p0 获取数据库连接池技术* @return Connection 数据库连接*/public static Connection getConnection1() throws Exception {// XML 文件应放置在直接命名的应用程序 CLASSPATH、WEB-INF/类或某些类似位置中的直接或 jar 文件中。// 文件名为:c3p0-config.xml// 文件路径:src/c3p0-config.xmlConnection conn = cpds.getConnection();System.out.println(conn);return conn;}/*** DBCP为了避免每次new对象都创建一个数据库连接池,所以这里使用静态代码块的方式初始化*/private static DataSource source;static {try {Properties pros = new Properties();FileInputStream fis = new FileInputStream(new File("src/jdbc/connection/dbcp.properties"));pros.load(fis);source = BasicDataSourceFactory.createDataSource(pros);} catch (Exception e) {e.printStackTrace();}}/*** 使用DBCP数据库连接技术获取数据库连接* @return* @throws Exception*/public static Connection getConnection2() throws Exception {return source.getConnection();}private static DataSource sourceDruid;static {try {Properties pros = new Properties();// ClassLoader.getSystemClassLoader().getResourceAsStream 的首路径为 src/InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc/connection/druid.properties");pros.load(is);sourceDruid = DruidDataSourceFactory.createDataSource(pros);} catch (Exception e) {e.printStackTrace();}}/*** 使用 Druid 数据库连接技术获取数据库连接* @return* @throws Exception*/public static Connection getConnection3() throws Exception {return sourceDruid.getConnection();}
}
<a name="dDs3x"></a>## 九、Apache-DBUtils实现CRUD操作- 代码```javapackage jdbc.dbutils;import jdbc.bean.Customer;import jdbc.util.JDBCUtils;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.ResultSetHandler;import org.apache.commons.dbutils.handlers.*;import org.junit.Test;import java.sql.Connection;import java.sql.Date;import java.sql.ResultSet;import java.sql.SQLException;import java.util.List;import java.util.Map;/*** 使用 apache-dbutils 封装的开源JDBC开源工具类,封装了增删改查*/public class QueryRunnerTest {/*** 测试插入*/@Testpublic void testInsert() {Connection conn = null;try {QueryRunner runner = new QueryRunner();conn = jdbc.connection.util.JDBCUtils.getConnection3();String sql = "insert into customers(name, email, birth) values(?, ?, ?)";int insertCount = runner.update(conn, sql, "蔡徐坤", "caixukun@163.com", "1997-02-23");System.out.println("添加了" + insertCount);} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}/*** 测试查询:* BeanHandler:ResultSetHandler接口的实现类,用于封装表中的一条记录*/@Testpublic void testQuery() throws Exception {QueryRunner runner = new QueryRunner();Connection conn = jdbc.connection.util.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, 18);System.out.println(customer);}/*** 测试查询多条记录:* BeanListHandler:ResultSetHandler接口的实现类,用于封装表中的一条记录*/@Testpublic void testQueryList() {Connection conn = null;try {QueryRunner runner = new QueryRunner();conn = jdbc.connection.util.JDBCUtils.getConnection3();String sql = "select id, name, email, birth from customers where id < ?";BeanListHandler<Customer> listHandler = new BeanListHandler<>(Customer.class);List<Customer> list = runner.query(conn, sql, listHandler, 18);list.forEach(System.out::println);} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}/*** MapHandler:对应表中的一条记录* 将字段及响应字段的值作为map中的key和value*/@Testpublic void testQueryMap() {Connection conn = null;try {QueryRunner runner = new QueryRunner();conn = jdbc.connection.util.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, 18);System.out.println(map);} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}/*** MapHandler:对应表中的一条记录* 将字段及响应字段的值作为map中的key和value* 将这些map添加到List*/@Testpublic void testQueryMapList() {Connection conn = null;try {QueryRunner runner = new QueryRunner();conn = jdbc.connection.util.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, 18);list.forEach(System.out::println);} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}@Testpublic void testQueryCount() {Connection conn = null;try {QueryRunner runner = new QueryRunner();conn = jdbc.connection.util.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 (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}/*** ScalarHandler 用于查询特殊值*/@Testpublic void testQueryMax() {Connection conn = null;try {QueryRunner runner = new QueryRunner();conn = jdbc.connection.util.JDBCUtils.getConnection3();String sql = "select max(birth) from customers";ScalarHandler handler = new ScalarHandler();Date birthMax = (Date) runner.query(conn, sql, handler);System.out.println(birthMax);} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}/*** 自定义 ResultSetHandler 实现类**/@Testpublic void testQueryCustomer() {Connection conn = null;try {QueryRunner runner = new QueryRunner();conn = jdbc.connection.util.JDBCUtils.getConnection3();String sql = "select id,name,email,birth from customers where id = ?";/*** JDK8 中:匿名实现类的泛型还不能省略*/ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>() {@Overridepublic Customer handle(ResultSet resultSet) throws SQLException {// 初始化// return null;// 制造虚拟// return new Customer(12, "陈龙", "chenglong@153.com", new Date(123123123123L));// 从数据库中获取if (resultSet.next()) {int id = resultSet.getInt("id");String name = resultSet.getString("name");String email = resultSet.getString("email");Date birth = resultSet.getDate("birth");Customer customer = new Customer(id, name, email, birth);return customer;}return null;}};Customer query = runner.query(conn, sql, handler, 18);System.out.println(query);} catch (Exception e) {e.printStackTrace();} finally {JDBCUtils.closeResource(conn, null);}}}
