JDBC和数据库连接池
JDBC概述
JDBC为访问不同的数据库提供了统一的接口,由数据库厂商实现这些接口,为使用者屏蔽了细节问题,Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种操作。相关的类和接口在java.sql和javax.sql包中
使用MYSQL的JDBC实现
使用MySQL的JDBC实现需要先引入对应的jar包,这里学习使用的是mysql-connector-java-5.1.37-bin.jar
操作数据基本步骤
- 注册驱动:加载Driver类
- 获取连接:得到Connection
- 执行增删改查:发送SQL给mysql执行
- 释放资源:关闭相关连接
import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class Connect01 {
public static void main(String[] args) throws SQLException {
// 注册驱动
Driver driver = new Driver();
String url = "jdbc:mysql://localhost:3306/lsg_db03";
Properties properties = new Properties();
properties.setProperty("user","root"); // 用户名
properties.setProperty("password","root"); // 密码
// 得到连接
Connection connect = driver.connect(url,properties);
// 执行sql语句
String sql = "insert into students values(12, '姜维', 54, 69, 78)";
Statement statement = connect.createStatement();
int rows = statement.executeUpdate(sql); // 返回值是改变行数
if (rows > 0){
System.out.println("成功");
} else {
System.out.println("失败");
}
// 关闭连接
statement.close();
connect.close();
}
}
注意:
- 注意Driver、Connection、Statement等相关类在哪个包下
- 这里的com.mysql.jdbc.Driver是实现了java.sql.Driver接口的类
- Connection是接口,运行类型是com.mysql.jdbc.JDBC4Connection
- Statement是接口,运行类型是class com.mysql.jdbc.StatementImpl
5种连接数据库方种
- 创建Driver对象注册驱动
public void connect01() throws SQLException {
// 注册驱动
Driver driver = new Driver();
String url = "jdbc:mysql://localhost:3306/lsg_db03";
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "root");
// 获取连接
Connection connect = driver.connect(url, properties);
// 释放资源
connect.close();
}
- 通过反射注册驱动
public void connect02() throws Exception {
// 加载Driver类,获取Driver类的Class对象
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
// 通过Driver的Class对象创建Driver实例
Driver driver = (Driver) aClass.newInstance();
String url = "jdbc:mysql://localhost:3306/lsg_db03";
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "root");
// 获取连接
Connection connect = driver.connect(url, properties);
// 释放资源
connect.close();
}
- 通过DriverManager管理Driver,并获取连接
public void connect03() throws Exception{
// 加载Driver类,获取Driver类的Class对象
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
// 通过Driver类的Class对象获取Driver实例
Driver driver = (Driver) aClass.newInstance();
// Driver实例交给DriverManager
DriverManager.registerDriver(driver);
String url = "jdbc:mysql://localhost:3306/lsg_db03";
String user = "root";
String password = "root";
// 通过DriverManager获取connection连接
Connection connection = DriverManager.getConnection(url, user, password);
// 释放资源
connection.close();
}
DriverManager全名是java.sql.DriverManager,是类,用于管理driver
- 通过DriverManager管理Driver,并获取连接(优化版)
public void connect04() throws Exception{
// 类加载时有静态代码块注册到DriverManager
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/lsg_db03";
String user = "root";
String password = "root";
// 获取连接
Connection connection = DriverManager.getConnection(url, user, password);
// 释放资源
connection.close();
}
类加载时有静态代码块注册到DriverManager
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
- 通过DriverManager管理Driver,读取配置文件获取连接信息,并获取连接(常用版)
public void connect05() throws IOException, ClassNotFoundException, SQLException {
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
// 读取配置文件信息
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
// 注册驱动
Class.forName(driver); // 可以不写,建议写上
// 获取连接
Connection connection = DriverManager.getConnection(url, user, password);
// 释放资源
connection.close();
}
注意:
- mysql驱动5.1.6可以无需Class.forName(“com.mysql.jdbc.Driver”)
- 因为从jdk1.5以后使用了jdbc4,不需要显示调用class.forName()去注册驱动,而是自动调用驱动jar包下META-INF\services\java.sql.Driver文本中的类名称去注册
- 但还是建议写上Class.forName(“com.mysql.jdbc.Driver”)
查询
查询结果返回到ResultSet中,这里的ResultSet是java.sql.ResultSet接口,运行类型是com.mysql.jdbc.JDBC42ResultSet
结果集中,最初光标位于第一行之前,ResultSet对象执行next方法,光标就会向下移动一行,当光标移动到最后一行后,再执行next方法会放回false,所以可以使用while循环遍历结果集
public static void main(String[] args) throws Exception {
// 读取配置信息
Properties properties = new Properties();
properties.load(new FileReader("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
// 注册驱动
Class.forName(driver); // 建议写上
// 获取连接
Connection connection = DriverManager.getConnection(url,user,password);
// 执行sql语句
Statement statement = connection.createStatement();
String sql = "select `uid`,`name`,`birthday` from `user`";
ResultSet resultSet = statement.executeQuery(sql);
// 遍历查询结果
while (resultSet.next()){
int uid = resultSet.getInt("uid");
String name = resultSet.getString("name");
Date birthday = resultSet.getDate("birthday");
System.out.println(uid + "\t" + name + "\t" + birthday);
}
// 释放资源
resultSet.close();
statement.close();
connection.close();
}
注意:
- ResultSet还有previous()方法,作用是让光标向上移动一行,如果没有上一行,返回false
- 如果没有查询到数据,ResultSet为null
预处理
前面执行sql语句都是用Statement(java.sql.Statement是接口,运行类型是class com.mysql.jdbc.StatementImpl)调用executeQuery方法或executeUpdate方法,这样虽然也可以执行sql语句,但是这样执行的sql语句必须是提前写好的,这样既不方便,又存在sql注入风险
可以通过把Statement替换为PreparedStatement(java.sql.PreparedStatement是接口,运行类型是com.mysql.jdbc.JDBC42PreparedStatement)有效解决这个问题
使用示例
PreparedStatement执行的SQL语句中的参数用占位符 ? 来表示,后面再调用setXxx()方法来设置这些参数
setXxx(参数位置,参数值):参数位置是指第几个参数(从1开始数)
// 模拟用户登录
public static void main(String[] args) throws Exception{
// 输入账号、密码
Scanner scanner = new Scanner(System.in);
System.out.print("请输入账号:");
String username = scanner.nextLine();
System.out.print("请输入密码:");
String pwd = scanner.nextLine();
// 读取配置文件信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
// 注册驱动
Class.forName(driver); // 建议写
// 获取连接
Connection connection = DriverManager.getConnection(url, user, password);
String sql = "select * from `user` where `username` = ? and `password` = ?";
// 获取PreparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 给参数赋值
preparedStatement.setString(1,username);
preparedStatement.setString(2,pwd);
// 执行sql语句
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()){
System.out.println("登录成功");
} else {
System.out.println("登录失败");
}
// 释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
注意:上面的代码,如果使用的Statement可以用sql注入登录成功
预处理好处
- 不在使用拼接字符串的方式写sql语句,减少语法cuow
- 有效的解决了sql注入问题
- 大大减少了编译次数,效率较高
JDBC工具类
JDBC每次操作数据库都要读取配置文件,获取连接、释放资源等重复操作,每次都写一遍代码冗余度太高,可以把这些重复用到的代码封装到一个工具类中
public class JDBCUtils {
private static String user;
private static String password;
private static String url;
private static String driver;
static {
// 静态代码块中读取配置文件
Properties properties = new Properties();
try {
properties.load(new FileReader("src\\mysql.properties"));
} catch (IOException e) {
// 把编译异常转成运行异常抛出
// 这时调用者可以选择捕获异常,也可以选择默认处理该异常,比较方便
throw new RuntimeException(e);
}
user = properties.getProperty("user");
password = properties.getProperty("password");
url = properties.getProperty("url");
driver = properties.getProperty("driver");
}
// 连接数据库,并返回连接
public static Connection getConnection(){
try {
return DriverManager.getConnection(url,user,password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
// 关闭相关资源
public static void close(ResultSet resultSet, Statement statement, Connection connection){
try {
if (resultSet != null){
resultSet.close();
}
if (statement != null){
statement.close();
}
if (connection != null){
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
事务
- JDBC程序中当一个Connection对象创建时,默认情况下是自动提交事务:每次执行一个SQL语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
- JDBC程序中为了让duogeSQL语句作为一个整体执行,需要使用事务。
- 调用Connection的setAutoCommit(false)可以取消自动提交事务。
- 在所有的SQL语句都成功执行后,调用commit() 方法提交事务。
- 当其中某个操作失败或出现异常时,调用rollback() 方法回滚事务
public static void main(String[] args) {
// 获取连接
Connection connection = JDBCUtils.getConnection();
PreparedStatement preparedStatement = null;
Savepoint point = null;
try {
// 取消自动提交事务——开启事务
connection.setAutoCommit(false);
// 执行sql语句
String sql = "update `users` set `balance` = `balance` - 100 where `id` = 1";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
// 设置保存点
point = connection.setSavepoint();
// 这里会抛出异常
int a = 5 / 0;
// 如果前面的异常,后面执行不到
// 执行又一条sql语句
sql = "update `users` set `balance` = `balance` + 100 where `id` = 2";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.executeUpdate();
// 所有语句正常执行,提交事务
connection.commit();
} catch (Exception throwables) { // 捕获到异常
try {
// 回滚事务
// connection.rollback(point);
connection.rollback(); // 回滚到开启事务位置
// 回滚后提交事务
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
throwables.printStackTrace();
} finally {
// 释放资源
JDBCUtils.close(null,preparedStatement,connection);
}
}
批处理
当需要成批插入或者更新记录时,如果sql语句一条一条地执行,效率特别低
可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理,通常情况下比单独提交更有效率
使用方法
- JDBC连接数据库的url后加参数:?rewriteBatchedStatements=true
- addBatch():把一条要执行的SQL语句存入PreparedStatement中的一个ArrayList集合中
- executeBatch():执行批量处理语句
- clearBatch():清空PreparedStatement中的存放SQL语句的ArrayList集合
public void batch() throws Exception{
// 获取连接
Connection connection = JDBCUtils.getConnection();
String sql = "insert into admin02 values (?,?)";
// 获取PreparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 1; i <= 500; i++) {
// 设置参数
preparedStatement.setInt(1,i);
preparedStatement.setString(2,"tom" + i);
// 把要执行的SQL语句存入PreparedStatement中的一个ArrayList集合中
preparedStatement.addBatch();
if (i%100 == 0){
// 执行批量处理语句
preparedStatement.executeBatch();
// 不必单独执行清空,执行批量处理语句后自动清空
// preparedStatement.clearBatch();
}
}
// 释放资源
JDBCUtils.close(null,preparedStatement,connection);
}
批处理往往和PreparedStatement一起搭配使用,可以即减少编译次数,又减少运行次数,效率大大提高
数据库连接池
引入连接池概念
传统连接数据库问题
- 传统JDBC数据库连接使用DriverManager来获取,每次向数据库建立连接的时候都要将Connection加载到内存中,再验证IP地址、用户名和密码。需要数据库连接的时候,就向数据库要求一个,频繁的进行数据库连接操作将占用很多系统资源,容易造成服务器崩溃。
- 每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终将导致重启数据库。
- 传统获取连接的方式,不能控制连接数量,如果连接过多,也可能导致内存泄漏,MySQL崩溃
连接池介绍
- 预先在缓冲池中放入一定数量的连接,当需要建立数据库时,只需从“缓冲池”中取出一个,使用完毕之后再放回去
- 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个
- 当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中
常用连接池
JDBC的数据库连接池使用java.sql.DataSource来表示,DataSource只是一个接口,该接口通常由第三方提供实现
- C3P0:速度相对较慢,稳定性不错
- DBCP:速度相对C3P0较快,但不稳定
- Proxool:有监控连接池状态的功能,稳定性较C3P0差一些
- BoneCP:速度快
- Druid(德鲁伊):是阿里提供的数据库连接池,集C3P0、DBCP、Proxool优点于一身的数据库连接池
C3P0
这里用的是c3p0-0.9.1.2.jar
用法
- 根据src下的c3p0-config.xml配置文件创建ComboPooledDataSource连接池对象
- 用ComboPooledDataSource连接池对象获取连接
// 连接测速代码
public void useXml() throws Exception{
// 根据xml文件创建连接池对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("mysql");
long start = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
// 从连接池取出连接
Connection connection = comboPooledDataSource.getConnection();
// 关闭连接:其实连接没有关闭,只是放回到连接池
connection.close();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
注意:
- ComboPooledDataSource实现了DataSource接口
- 这里取出的connection的运行类型是com.mchange.v2.c3p0.impl.NewProxyConnection
- connection.close() 并不会真的把连接关闭,只是放回了连接池
配置文件
src下的c3p0-config.xml配置文件
<c3p0-config>
<!-- 数据源名称,代表连接池 -->
<named-config name="mysql">
<!-- 配置数据库用户名 -->
<property name="user">root</property>
<!-- 配置数据库密码 -->
<property name="password">root</property>
<!-- 配置数据库链接地址 -->
<property name="jdbcUrl">jdbc:mysql://localhost:3306/lsg_db02</property>
<!-- 配置数据库驱动 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<!-- 每次增长的连接数 -->
<property name="acquireIncrement">5</property>
<!-- 初始化连接数 -->
<property name="initialPoolSize">10</property>
<!-- 最小连接数 -->
<property name="minPoolSize">5</property>
<!--连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize">30</property>
<!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default:0 -->
<property name="maxStatements">2</property>
<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
配置文件为properties情况(少用)
java代码
public void useProperties() throws Exception{
Properties properties = new Properties();
properties.load(new FileReader("src\\mysql.properties"));
String user = properties.getProperty("user");
String url = properties.getProperty("url");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
comboPooledDataSource.setJdbcUrl(url);
// 设置初始化连接数
comboPooledDataSource.setInitialPoolSize(10);
// 设置最大连接数
comboPooledDataSource.setMaxPoolSize(500);
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
properties配置文件
user=root
password=root
url=jdbc:mysql://localhost:3306/lsg_db02?rewriteBatchedStatements=true
driver=com.mysql.jdbc.Driver
Druid(德鲁伊)
这里用的是druid-1.1.22.jar
用法
- 加载配置文件
- 根据配置文件用DruidDataSourceFactory创建DataSource连接池对象
- 在连接池对象中取出连接
// 测试连接速度代码
public static void main(String[] args) throws Exception {
// 加载配置文件
Properties properties = new Properties();
properties.load(new FileReader("src\\druid.properties"));
// 根据配置文件用DruidDataSourceFactory创建DataSource连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
long start = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
// 从连接池中取出连接
Connection connection = dataSource.getConnection();
// 关闭连接:其实连接没有关闭,只是放回到连接池
connection.close();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
注意:
- DataSource是接口,运行类型是com.alibaba.druid.pool.DruidDataSource
- connection的运行类型是com.alibaba.druid.pool.DruidPooledConnection
配置文件
# 驱动名称
driverClassName=com.mysql.jdbc.Driver
# 连接地址
url=jdbc:mysql://localhost:3306/lsg_db02?rewriteBatchedStatements=true
# 账号
username=root
# 密码
password=root
# 初始连接数
initialSize=10
# 最大连接数
maxActive=20
# 最小连接数
minIdle=5
# 最长等待时间
maxWait=1000
德鲁伊工具类
和JDBC工具类一样,虽然获取连接的方式不同了,但是根据减少冗余代码的思想仍可以编写工具类
public class JDBCUtilsByDruid {
private static DataSource dataSource;
static { // 静态代码块读取配置文件,并初始化连接池
Properties properties = new Properties();
try {
properties.load(new FileReader("src\\druid.properties"));
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// 获取连接方法
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
// 关闭连接
public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){
try {
if (connection != null){
connection.close();
}
if (preparedStatement != null){
preparedStatement.close();
}
if (resultSet != null){
resultSet.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
DBUtils
这里用的是commons-dbutils-1.3.jar
原生的操作数据代码比较麻烦,并要解决一些问题,比如:查询语句查询到结果是放在ResultSet对象中,但是如果connection关闭,这个ResultSet对象就不能再使用,我们想要较长时间保存,就要先把这里的数据提前另存到别的对象中
现在有一个工具类类库,帮我们做了数据迁移的功能,当然这个类库的功能不止这一个
commons-dbutils是Apache组织提供的一个开源JDBC工具类库,它是对JDBC的封装,使用dbutils能极大简化jdbc编码的工作量
处理查询操作
用法
- 创建org.apache.commons.dbutils.QueryRunner对象
- QueryRunner对象调用query方法执行sql语句(是线程安全的)
public static void main(String[] args) throws SQLException {
// 获取连接
Connection connection = JDBCUtilsByDruid.getConnection();
// 创建QueryRunner对象
QueryRunner queryRunner = new QueryRunner();
String sql = "select `uid`,`name`,`birthday` from `user` where `uid` != ?";
// 执行sql语句
List<User> users = queryRunner.query(connection, sql, new BeanListHandler<>(User.class),1);
for (User user : users) { // 遍历查询结果
System.out.println(user.getUid() + "\t" + user.getName() + "\t" + user.getBirthday());
}
// 释放资源
JDBCUtilsByDruid.close(connection,null,null);
}
关于query方法
共四个参数
- 第一个参数:connection连接
- 第二个参数:sql语句
- 第三个参数:ResultSetHandler接口,根据传入不同的子类作为参数决定返回类型
- 第四个参数:可变参数,用于给sql语句中的占位符赋值,如果没有占位符,则不写这个参数
第三个参数子类可选
- ArrayHandler:把结果集中的第一行数据转成对象数组
- ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中
- BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中
- BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里
- ColumnListHandler:将结果集中某一列存放到List中
- KeyedHandler(name):将结果集中的每行数据都封装到Map里,再把这些map存放到一个map里,其key为指定的key
- MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值
- MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
- ScalarHandler:将结果集中的第一行、第一列的数据返回,返回类型为Object
源码分析
query方法底层会创建PreparedStatement执行sql语句,并获取ResultSet结果集,不过在query方法中就会把他们关闭
public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
PreparedStatement stmt = null;
ResultSet rs = null;
Object result = null;
try {
// 根据connection获取PreparedStatement,里面是conn.prepareStatement(sql)
stmt = this.prepareStatement(conn, sql);
// 把可变参数赋给指定的占位符
this.fillStatement(stmt, params);
// 执行sql语句,并把ResultSet结果集返回,wrap方法中直接返回结果集,没有任何操作
rs = this.wrap(stmt.executeQuery());
// 把ResultSet结果集转成JavaBean的List集合(如果res是BeanListHandler)
result = rsh.handle(rs);
} catch (SQLException var33) {
this.rethrow(var33, sql, params);
} finally {
try {
// 关闭ResultSet
this.close(rs);
} finally {
// 关闭PreparedStatement
this.close((Statement)stmt);
}
}
// 返回结果
return result;
}
处理DML操作
用法
- 创建org.apache.commons.dbutils.QueryRunner对象
- QueryRunner对象调用update方法执行sql语句
public static void main(String[] args) throws SQLException {
// 获取连接
Connection connection = JDBCUtilsByDruid.getConnection();
// 创建QueryRunner对象
QueryRunner queryRunner = new QueryRunner();
String sql = "insert into `users` values(?, ? ,?)";
// 执行sql语句,返回值是受影响的行数
int rows = queryRunner.update(connection, sql, 44, "宫本武藏", 600);
if (rows > 0){
System.out.println("执行成功");
}
// 释放资源
JDBCUtilsByDruid.close(connection,null,null);
}
update源码
update方法源码类似query方法,不重复分析bn
public int update(Connection conn, String sql, Object... params) throws SQLException {
PreparedStatement stmt = null;
int rows = 0;
try {
stmt = this.prepareStatement(conn, sql);
this.fillStatement(stmt, params);
rows = stmt.executeUpdate();
} catch (SQLException var10) {
this.rethrow(var10, sql, params);
} finally {
this.close((Statement)stmt);
}
return rows;
}
BasiDao
DAO:data access object 数据访问对象
把操作数据库的共同操作,放入BasicDao里,可以简化代码,提高维护性和可读性
编写示例
BasicDao
public class BasicDao<T> {
private QueryRunner queryRunner = new QueryRunner();
/**
* dml方法
* @param sql
* @param parameters
* @return
*/
public int update(String sql, Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
int update = queryRunner.update(connection, sql, parameters);
return update;
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsByDruid.close(connection,null,null);
}
}
/**
* 查询多条数据
* @param sql
* @param clazz
* @param parameters
* @return
*/
public List<T> queryMulti(String sql, Class<T> clazz, Object... parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
return queryRunner.query(connection, sql, new BeanListHandler<>(clazz), parameters);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsByDruid.close(connection,null,null);
}
}
/**
* 查询一条数据
* @param sql
* @param clazz
* @param parameters
* @return
*/
public T querySingle(String sql, Class<T> clazz, Object... parameters){
Connection connection = JDBCUtilsByDruid.getConnection();
try {
return queryRunner.query(connection,sql,new BeanHandler<>(clazz),parameters);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsByDruid.close(connection,null,null);
}
}
/**
* 查询单行单列数据
* @param sql
* @param parameters
* @return
*/
public Object queryScalar(String sql, Object... parameters){
Connection connection = JDBCUtilsByDruid.getConnection();
try {
return queryRunner.query(connection,sql,new ScalarHandler(),parameters);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsByDruid.close(connection,null,null);
}
}
}
UserDao
在BasicDao基础上编写具体的操作
public class UserDao extends BasicDao<User>{
public int insertUser(User user){
if (user == null) return -1;
String sql = "insert into `users` values(?,?,?)";
return update(sql,user.getId(),user.getName(),user.getBalance());
}
public List<User> selectAll(){
return queryMulti("select * from `users`", User.class);
}
}
测试代码
public class DaoTest {
@Test
public void test1(){
// 插入一条数据
UserDao userDao = new UserDao();
int rows = userDao.insertUser(new User(4, "李云龙", 800));
if (rows > 0){
System.out.println("插入成功");
}
}
@Test
public void test2(){
// 查询表中所有数据
UserDao userDao = new UserDao();
List<User> users = userDao.selectAll();
if (users != null){
for (User user : users) {
System.out.println(user);
}
}
}
}