一、JDBC概述
基本介绍:
1.JDBC为访问不同的数据库提供了统一的接口(标准),为使用者屏蔽了细节问题
2.Java程序员使用 JDBC,可以连接任何提供了 JDBC驱动程序的数据库系统,从而完成对数据库的各种操作。
3.JDBC原理图:
JDBC API:
JDBC是 Java提供一套用于操作数据库操作的接口API,Java程序员只需要面向这套接口编程即可。不同的数据库厂商,需要针对这套接口,提供不同的实现,相关类和接口在java.sgl与javax.sal包,
二、JDBC快速入门
1.注册驱动 - 加载Driver类
2.获取连接 - 得到Connection
3.执行增删改查 - 发送SQL给mysql执行
4.释放资源 - 关闭相关的连接等
2.1 JDBC第一个程序
public class jdbc01 {
public static void main(String[] args) throws SQLException {
//前置工作,在项目创建文件夹,如 libs,复制 mysql-connector-java.jar到该文件夹下,并加入到项目中
// 1.注册驱动
Driver driver = new Driver(); // 创建driver对象
// 2.得到连接
// (1) jdbc:mysql:// 【规定好的,协议,通过jdbc的方式来连接mysql】
// (2) localhost: 【localhost表示本机,主机。可以是ip地址(非本机)】
// (3) 3306 【端口号,可能有不同的端口号。如3307】
// (4) /db1_test 【要操作的具体数据库】
String url = "jdbc:mysql://localhost:3306/db1_test";
// 将用户名和密码放入到Properties对象中
Properties properties = new Properties();
// 关键字:user、password
properties.setProperty("user","root"); // 设置用户
properties.setProperty("password","xxx"); // 密码
Connection connect = driver.connect(url, properties);
// 3.执行sql
String sql = "INSERT INTO employees() VALUES(12,'小明','15000','男'),(11,'小红','10000','女')";
// Statement对象(操作数据库的对象) 用于执行静态sql语句,并返回其生成的结果的对象
Statement statement = connect.createStatement();
int rows = statement.executeUpdate(sql); // 大于0,返回影响行数。否则执行失败
System.out.println(rows>0?"成功":"失败");
// 4.关闭连接资源
statement.close();
connect.close();
}
}
2.2 获得数据库连接的五种方式
第一种:
静态加载Driver类
public void connect01() throws SQLException {
// 获取Driver实现类对象
Driver driver = new com.mysql.jdbc.Driver()
// 连接地址
String url = "jdbc:mysql://localhost:3306/db1_test";
// 用户: 用户名和密码
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","xxx");
// 填入 连接地址(url)和 用户(info),得到连接
Connection conn = driver.connect(url,info);
System.out.println(conn);
}
第二种:
动态加载Driver类,使用反射加载
// 方式 1会直接使用 com.mysql.jdbc.Driver0,属于静态加载,灵活性差,依赖强
// --推出-->方式2
public void connect02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
String url = "jdbc:mysql://localhost:3306/db1_test";
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","xxx");
Connection conn = driver.connect(url,info);
System.out.println(conn);
}
第三种:
使用DriverManager替代Diver进行统一管理
public void connect03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
// 使用反射加载Driver
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) aClass.newInstance();
// 创建 url 和 user 和 password
String url = "jdbc:mysql://localhost:3306/db1_test";
String user = "root";
String password = "xxx";
DriverManager.registerDriver(driver); // 注册Driver驱动
// 得到连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
第四种:
使用Class.forName自动完成注册驱动,简化代码=>分析源码
public void connect04() throws ClassNotFoundException, SQLException {
// 使用反射加载了 Driver类
Class.forName("com.mysql.jdbc.Driver");
// 创建 url 和 user 和 password
String url = "jdbc:mysql://localhost:3306/db1_test";
String user = "root";
String password = "xxx";
// 得到连接
Connection conn = DriverManager.getConnection(url,user,password);
System.out.println(conn);
}
MySQL驱动5.1.6可以无需CLass.forName(“com.miysql.jdbc.Driver");从jdk1.5以后使用了jdbc4,不再需要显示调用class.forName(0注册驱动而是自动调用驱动jar包下META-INF\services\java.sql.Driver文本中的类名称去注册
但建议还是写上CLass.forName('com.mysql.jdbc.Driver",更加明确
第五种(开发建议使用):
方式5,在方式4的基础上改进,增加配置文件,让连接ysqL更加灵活
配置properties文件: 复制到 src目录下
user=root
password=xxx
url=jdbc:mysql://Localhost:3306/db1_test
driver=com.mysql.jdbc.Driver
public static void connect05() throws IOException, ClassNotFoundException, SQLException {
// 通过Properties,对象获取配置文件的信息
Properties properties = new Properties();
properties.load(new FileInputStream("test_sql\\src\\mysql.properties"));
// 获取相关的值
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url,user,password);
System.out.println(connection);
}
2.3 结果集
2.4 Statement接口
Statement是一个接口,位于 java.sql包下,它被不同的厂商实现
// 返回的是实现了Statement接口的实例化对象,不同的厂商的返回的实现类对象不同
Statement statement = connection.createStatement();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
2.4.1 Statement(实现类对象)
2.4.2 PreparedStatement(预处理)
String sql = “SELECT COUNT(*) FROM admin WHERE username = ? AND PASSWORD=?”;
1.PreparedStatement执行的SQL语句中的参数用问号(?)来表示,调用PreparedStatement对象的setXxx0方法来设置这些参数.setXxx0方法有两个参数,第一个参数是要设置的SQL语句中的参数的索引(从1开始),第二个是设置的SQL语句中的参数的值
2.调用executeQuery(),返▣ResultSet对象
3.调用executeUpdate0:执行更新,包括增、删、修改
预处理好处
1.不再使用 +拼接 sq语句,减少语法错误
2.有效的解决了 sql注入问题!
3.大大减少了编译次数,效率较高
...
// 组织 sql,sql中的 ? 相当于占位符
String sql = "select * from employees where user = ? and password = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 给占位符赋值
prepareStatement.setString(1,"root");
prepareStatement.setString(2,"xxx");
// 执行select语句使用executeQuery
// 如果执行的是 增删改 DML(update,insert,delete),用 executeUpdate()
// 这里不用写sql,如果写了就是原来的sql,字符串的不变性
ResultSet resultSet = prepareStatement.executeQuery();
...
...
String sql = "insert into admin values(?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
prepareStatement.setString(1,"1");
prepareStatement.setString(2,"这是内容");
ResultSet resultSet = prepareStatement.executeUpdate();
...
2.4 JDBC API 学习小结
返回的实例化对象,都是厂商实现了该类的实现类对象
三、JDBCUtils工具类
在dbc操作中,获取连接和释放资源是经常使用到可以将其封装DBC连接的工具类JDBCUtils
JDBCUtils是一个工具类,完成 mysql的连接和关闭
public class JDBCUtils {
// 定义相关的属性(4个),因为只需要一份,因此,我们做出static
public static String url; // url
public static String user; // 用户
public static String password; // 密码
public static String driver; // 驱动
// static 代码块初始化
static {
Properties properties = new Properties();
try {
// 加载配置文件
properties.load(new FileInputStream("test_sql\\src\\mysql.properties"));
// 读取属性值
String url1 = properties.getProperty("url");
String user1 = properties.getProperty("user");
String password1 = properties.getProperty("password");
String driver1 = properties.getProperty("driver");
} catch (IOException e) {
// 实际开发可以这样处理
// 1.将编译异常转为运行异常
// 2.这时候调用者 可以选择捕获该异常,也可以选择默认处理该异常,比较方便。
throw new RuntimeException(e);
}
}
// 连接数据库,返回 Connection
public static Connection getConnection() {
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/* 关闭资源
1.resultSet 结果集
2.Statement 或者 PreparedStatement
3.Connection
4.需要关闭传入 null,否则传入对象
*/
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);
}
}
}
public class Main {
public static void testDML(){ // insert、update、delete
Connection connection = null;
String sql = "update news set content = ? where id = ?";
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "q");
preparedStatement.setInt(2, 1);
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(null, preparedStatement, connection);
}
}
}
四、事务处理
基本介绍
1.JDBC程序中当一个 Connection对像创建时,默认情况下是自动提交事务:每次执行一个>SQL语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
2.JDBC程序中为了让多个SQL语句作为一个整体执行,需要使用事务
3.调用 Connection的 setAutoCommit(false)可以取消自动提交事务
4.在所有的SQL语句都成功执行后,调用 Connection的 commit()方法提交事务
5.在其中某个操作失败或出现异常时,调用 Connection的 rollback()方法回滚事务
示例: (把张三的100给到李四,这时候为了数据的一致性,需要用到事务处理)
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(32) NOT NULL DEFAULT '',
balance DOUBLE NOT NULL DEFAULT 0) CHARACTER SET utf8;
INSERT INTO account VALUES(NULL,'张三', 3000);
INSERT INTO account VALUES(NULL,'李四', 10000);
public class transaction01 {
public static void use_transaction() {
Connection connection = JDBCUtils.getConnection();
PreparedStatement preparedStatement = null;
PreparedStatement preparedStatement1 = null;
try {
connection.setAutoCommit(false); // connection执行 sql语句默认自动提交。设置为 false开启事务
String sql1 = "update account set balance = balance - 100 where id = 3";
String sql2 = "update account set balance = balance + 100 where id = 4";
preparedStatement = connection.prepareStatement(sql1);
preparedStatement.executeUpdate(); // 执行第一条
// int i = 1/0; 异常
preparedStatement1 = connection.prepareStatement(sql2);
preparedStatement1.executeUpdate(); // 执行第二条
connection.commit();
} catch (Exception e) {
try {
System.out.println("执行发生异常,回滚sql语句");
connection.rollback(); // 当出现异常时,catch捕获,执行回滚操作默认回滚到事务开始的,可以设置保存点(connection.setSavepoint("a保存点");)
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}finally {
try {
preparedStatement.close();
preparedStatement1.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
use_transaction();
}
}
五、批处理
基本介绍
1.当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允
许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。
2.JDBC的批量处理语句包括下面方法:
addBatch(:添加需要批量处理的SQL语句或参数
executeBatch():执行批量处理语句;
clearBatch():清空批处理包的语句
3.JDBC连接 MySQL时,如果要使用批处理功能,需要在 url中加入参数
?rewriteBatchedStatements=true
4.批处理往往和 PreparedStatement 一起搭配使用,可以既减少编译次数,又减少运行次数,效率大大提高
示例:
未使用批处理,往 news表中添加5000条数据:
CREATE TABLE news(
id INT,
content VARCHAR(10)
);
public class Batch01 {
public static void main(String[] args) throws SQLException {
Connection connection = JDBCUtils.getConnection();
String sql = "insert into news values (?, ?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
long start = System.currentTimeMillis();
for(int i=1;i<=5000;i++){
preparedStatement.setInt(1,i);
preparedStatement.setString(2,"新闻内容");
preparedStatement.executeUpdate();
}
long end = System.currentTimeMillis();
System.out.println("执行时间: "+ (end - start) + "ms");
/* 执行时间: 6804ms */
JDBCUtils.close(null, preparedStatement, connection);
}
}
使用批处理:
public class Batch01 {
public static void main(String[] args) throws SQLException {
Connection connection = JDBCUtils.getConnection();
String sql = "insert into news values (?, ?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
long start = System.currentTimeMillis();
for(int i=1;i<=5000;i++){
preparedStatement.setInt(1,i);
preparedStatement.setString(2,"新闻内容");
// 将 sql语句加入批处理包中
preparedStatement.addBatch();
// 当有 1000条 sql语句时,将批处理包发送给 mysql执行
if(i%1000 == 0){
preparedStatement.executeBatch(); // 批量执行
preparedStatement.clearBatch(); // 清空
}
}
long end = System.currentTimeMillis();
System.out.println("执行时间: "+ (end - start) + "ms");
/* 执行时间: 139ms */
JDBCUtils.close(null, preparedStatement, connection);
}
}
可以发现,执行速度提升了近50倍.
六、数据库连接池
传统 Connection问题分析:
1.频繁的进行数据库连接操作将占用很多的系统资源,容易造成服务器崩溃。
2.每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终将导致重启数据库。
3.传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致内存泄漏,MySQL崩溃。
4.解决传统开发中的数据库连接问题(无缓冲),可以采用数据库连接池技术(connection pool)
数据库连接池基本介绍:
1.预先在 缓冲池 中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
2.数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
3.当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列
6.1 数据库连接池种类
1.JDBC的数据库连接池使用 javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由第三方提供实现(不同的数据库连接池技术实现方式不一样)
2.C3P0数据库连接池,速度相对饺慢,稳定性不错(hibernate,spring)
3.DBCP数据库连接池,速度相对c3p0较快,但不稳定
4.Proxool数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
5.BoneCP数据库连接池,速度快
6.Druid(德鲁伊) 是阿里提供的数据库连接池,集DBCP、C3P0、Proxool优点于一身的数据库连接池
6.2 C3P0 数据库连接池
方式一:相关的参数在程序中指定(url,user,password等)
public class c3p0_test {
public static void test() throws IOException, PropertyVetoException, SQLException {
// 加入c3p0jar包,properties配置文件
// 1.创建一个数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
// 2.通过配置文件(mysql.properties)获取相关的信息
Properties properties = new Properties();
properties.load(new FileInputStream("test_sql\\src\\mysql.properties"));
// 3.获取相关的属性值
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
// 4.给数据源 comboPooledDataSource设置参数(连接的创建和管理交给comboPooledDataSource)
comboPooledDataSource.setJdbcUrl(url); // url
comboPooledDataSource.setUser(user); // user
comboPooledDataSource.setPassword(password); // password
comboPooledDataSource.setDriverClass(driver); // driver
comboPooledDataSource.setInitialPoolSize(5); // 设置初始化连接数
comboPooledDataSource.setMaxPoolSize(50); // 设置最大连接数(超出最大连接数时的连接,会进入等待队列。不会创建新的连接)
// 5.获得连接
Connection connection = comboPooledDataSource.getConnection();// 实现 DataSource接口的方法
System.out.println("连接成功" + connection);
connection.close();
}
public static void main(String[] args) throws PropertyVetoException, SQLException, IOException {
test();
}
}
方式二: 使用配置文件模板来完成
配置文件模板:
<?xml version="1.0" encoding="utf-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
<property name="user">root</property>
<property name="password">xxx</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">50</property>
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="ihihr">
</named-config>
</c3p0-config>
public class c3p0_test02 {
public static void test() throws SQLException {
// 将c3p0提供的c3p0.config.XmL拷贝到src目录下(该文件指定了连接数据库和连接池的相关参数)
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("ihihr");
Connection connection = comboPooledDataSource.getConnection();
System.out.println(connection);
}
public static void main(String[] args) throws SQLException {
test();
}
}
6.3 Druid(德鲁伊)数据库连接池
配置文件模板:
#key=value
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://Localhost:3306/test?rewriteBatchedstatements=true
#url=jdbc:mysql://Localhost:3306/test
username=root
password=xxx
#initial connection Size
initialsize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=50
#max wait time (5000 mil seconds)
maxWait=5000
public class Druid01 {
public static void test() throws Exception {
//1.加入Druid jar包
//2.加入配置文件dru1d.properties,将该文件拷贝项目的src目录
//3.创建properties对象读取配置文件
Properties properties = new Properties();
// 当有多个子项目时,需要指明是那个子项目的 src目录
properties.load(new FileInputStream("test_sql\\src\\druid.properties"));
//4.创建一个指定配置文件参数的数据源(数据库连接池)
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//5.得到连接
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
public static void main(String[] args) throws Exception {
test();
}
}
6.4 JDBCUtils类(德鲁伊实现)
public class JdbcUtils{
private static DataSource dataSource;
// 静态代码块完成初始化
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("test_sql\\src\\druid.properties"));
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
// 得到连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
// 关闭连接(放回连接池)(Connection是接口! 这里的connection.close()方法
// 并不是原来mysql实现的connection.close()方法,而是Druid(德鲁伊)实现的connection.close()方法,仅仅只是放回连接池)
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);
}
}
}
或者:
public class JdbcUtils {
private static DruidDataSource dataSource;
// 静态代码块完成初始化
static {
Properties properties = new Properties();
try {
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
properties.load(inputStream);
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
// 得到连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
// 关闭连接(放回连接池)(Connection是接口! 这里的connection.close()方法
// 并不是原来mysql实现的connection.close()方法,而是Druid(德鲁伊)实现的connection.close()方法,仅仅只是放回连接池)
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);
}
}
}
七、ApDBUtils工具类
引入:
1.关闭 connection后,resultSet结果集无法使用
2.resultSet不利于数据的管理
3.使用结果集中的数据也不方便(getInt(…)、getString(…))
表对应类:JavaBean类(Domain类,POJO类),一个对象对应一条记录,再将对象(记录)封装到 ArrayList<person>中,
Apache–DBUtils类库
基本介绍:
1.commons-dbutils是Apache组织提供的一个开源JDBC工具类库,它是对JDBC的封装,使用dbutils能极大简化jdbc编码的工作量。
DbUtils类:
1.QueryRunner类:该类封装了SQL的执行,是线程安全的。可以实现增、删、改、查、批处理
2.使用QueryRunner类实现查询
3.sultSetHandler接口:该接口用于处理java.sql.ResultSet,将数据按要求转换为另一种形式
ArrayHandler: 把结果集中的第一行数据转成对象数组。
ArrayListHandler: 把结果集中的每一行数据都转成一个数组,再存放到List中。
BeanHandler: 将结果集中的第一行数据封装到一个对应的JavaBean实例中。
BeanListHandler: 将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
ColumnListHandler: 将结果集中某一列的数据存放到List中。
KeyedHandler(name): 将结果集中的每行数据都封装到Map里,再把这些map再存到一个map里,其key为指定的key。
MapHandler: 将结果集中的第一行数据封装到一个Map里,key是列名,vaue就是对应的值。
MapListHandler: 将结果集中的每一行数据都封装到一个Map里,然后再存放到List
示例: (使用 DBUtils + 德鲁伊(数据库存连接池)的方式完成对 news表的增、删、查、改)
news表:
CREATE TABLE news(
INT id,
content VARCHAR(10)
);
properties配置文件:
#key=value
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://Localhost:3306/test?rewriteBatchedstatements=true
#url=jdbc:mysql://Localhost:3306/test
username=root
password=xxx
#initial connection Size
initialsize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=50
#max wait time (5000 mil seconds)
maxWait=5000
引用的库:
[commons-dbutils-1.7.jar] 、[druid-1.0.10.jar] 与 [mysql-connector-java-8.0.29.jar]
news–JavaBean类:
public class news {
private Integer id;
private String content;
public news(){
}
public news(Integer id, String content) {
this.id = id;
this.content = content;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "news{" +
"id=" + id +
", content='" + content + '\'' +
'}';
}
}
JDBCUtilsByDruid工具类:
public class JDBCUtilsByDruid {
private static DataSource dataSource;
// 静态代码块完成初始化
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("test_sql\\src\\druid.properties"));
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
// 得到连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
// 关闭连接(放回连接池)(Connection是接口! 这里的connection方法并不是原来mysql实现的connection方法,而是Druid(德鲁伊)实现的connection方法)
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);
}
}
}
7.1 测试-查询
查询(返回多条记录)
public class dbutils_druid01 {
// 返回多条结果
public static void testQueryMany() throws SQLException {
// 使用DBUtils类和接口,引入DBUtils相关的jar,加入到本Project
// 1.得到连接
Connection connection = JDBCUtilsByDruid.getConnection();
// 2.创建QueryRunner
QueryRunner queryRunner = new QueryRunner();
// 3.执行相关的方法,返回ArrayList结果集
String sql = "select * from news where id >= ?";
//(1) query方法就是执行sgL语句,得到resultset---封装到-->ArrayList集合中
//(2) 返回集合
//(3) connection:连接
//(4) sgL:执行的sgL语句
//(5) new BeanListHandler<>(Actor,class):在将resultset-> Actor对象->封装到ArrayList
// 底层使用反射机制 去获取Actor进行封装
//(6) 1是给sqL语句中的?赋值,可以有多个值,因为是可变参数
//(7) 底层得到的resultSet方法,会在query方法中关闭, 还会关闭PreparedStatement,因为底层就是用PreparedStatement完成dml
List<news> list = queryRunner.query(connection, sql, new BeanListHandler<>(news.class), 1);
for(news n:list){
System.out.println(n);
}
JDBCUtils.close(null, null, connection);
}
public static void main(String[] args) throws SQLException {
testQueryMany();
}
}
查询(返回单条记录)
...
String sql = "select * from news where id = ?";
QueryRunner queryRunner = new QueryRunner();
// 因为返回的是单个记录, 使用的 Handler是 BeanHandler
news ne = queryRunner.query(connection, sql, new BeanHandler<>(news.class), 2);
...
查询(返回单行单列)
...
String sql = "select content from news where id = ?";
QueryRunner queryRunner = new QueryRunner();
// Scalar表示的意思就是单行单列,返回 Object类型
Object obj = queryRunner.query(connection, sql, new ScalarHandler<>(), 2);
...
7.2 测试-DML
public class dbutils_druid03 {
public static void testDML() throws SQLException {
Connection connection = JDBCUtilsByDruid.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql = "update news set content = ? where id = ?";
String sql2 = "insert into news values (?, ?)";
String sql3 = "delete from news where id = ?";
//(1) 执行 DML操作的是 update()方法
//(2) 返回影响行数(affected: 受影响)
int affectedRow = queryRunner.update(connection, sql, "新闻", 1);
System.out.println(affectedRow>0?"执行成功":"执行没有影响到表");
int affectedRow2 = queryRunner.update(connection, sql2, 3, "内容3");
System.out.println(affectedRow2>0?"执行成功":"执行没有影响到表");
int affectedRow3 = queryRunner.update(connection, sql3, 3);
System.out.println(affectedRow2>0?"执行成功":"执行没有影响到表");
JDBCUtilsByDruid.close(null, null, connection);
}
public static void main(String[] args) throws SQLException {
testDML();
}
}
八、DAO和BasicDao
引入BasicDao:
apache-dbutils + Druid简化了JDBC开发,但还有不足:
1.SQL语句是固定,不能通过参数传入,通用性不好,需要进行改进,更方便执行增删改查
2.对于select操作,如果有返回值,返回类型不能固定,需要使用泛型
3.将来的表很多,业务需求复杂,不可能只靠一个Java类完成
Dao: 进行数据操作的类, 是对于数据库中的表的数据做增删改查等操作的类。
基本说明:
1.DAO:data access obje数据访问对象
2.这样的通用类,称为BasicDao,是专门和数据库交互的,即完成对数据库(表)的crud操作。
3.在BaiscDao的基础上,实现一张表对应一个Dao,更好的完成功能,比如Customer表-
Customer.java(javabean)-CustomerDao.java
示例:
在开发中,通常分为三层架构:
数据层访问层 – MySQL数据库、JavaBean类、、dao类
service层(业务逻辑层)
view层(界面层)
以下示例:dao类
8.1 BasicDao类(通用方法类)
public abstract class BasicDao<T> {
private QueryRunner qr = new QueryRunner();
// 开发通用的 dml操作方法
public int update(String sql, Object... parameters) {
Connection connection = null;
try {
connection = JdbcUtils.getConnection();
int update = qr.update(connection, sql, parameters);
return update;
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.close(null, null, connection);
}
}
/**
* 返回多个对象(多行)的通用方法
* @param sql sql语句,可以有 ?
* @param clazz 传入一个类的 Class对象,比如:news.class
* @param parameters 传入 ? 的具体的值,可以是多个
* @return 根据 news.class 返回对应的 ArrayList集合
*/
public List<T> queryMany(String sql, Class<T> clazz, int parameters){
Connection connection = null;
try {
connection = JdbcUtils.getConnection();
return qr.query(connection, sql, new BeanListHandler<>(clazz), parameters); // List<T> query
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.close(null, null, connection);
}
}
// 查询单行结果的通用方法
public T querySingle(String sql, Class<T> clazz, Object... parameters){
Connection connection = null;
try {
connection = JdbcUtils.getConnection();
return qr.query(connection, sql, new BeanHandler<>(clazz), parameters); // T Query
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.close(null, null, connection);
}
}
// 返回单行单列的通用方法
public Object queryScalar(String sql, Object... parameters){
Connection connection = null;
try {
connection = JdbcUtils.getConnection();
return qr.query(connection, sql, new ScalarHandler<>(), parameters);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JdbcUtils.close(null, null, connection);
}
}
}
或者:
public abstract class BasicDao<T> {
private QueryRunner qr = new QueryRunner();
// 通用的 dml操作方法
public int update(String sql, Object... parameters) {
Connection connection = null;
try {
connection = JdbcUtils.getConnection();
return qr.update(connection, sql, parameters);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(null, null, connection);
}
return -1;
}
/**
* 返回多个对象(多行)的通用方法
* @param sql sql语句,可以有 ?
* @param clazz 传入一个类的 Class对象,比如:news.class
* @param parameters 传入 ? 的具体的值,可以是多个
* @return 根据 news.class 返回对应的 ArrayList集合,执行失败返回null
*/
public List<T> queryMany(String sql, Class<T> clazz, int parameters){
Connection connection = null;
try {
connection = JdbcUtils.getConnection();
return qr.query(connection, sql, new BeanListHandler<>(clazz), parameters); // List<T> query
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(null, null, connection);
}
return null;
}
// 查询单行结果的通用方法
public T querySingle(String sql, Class<T> clazz, Object... parameters){
Connection connection = null;
try {
connection = JdbcUtils.getConnection();
return qr.query(connection, sql, new BeanHandler<>(clazz), parameters); // T Query
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(null, null, connection);
}
return null;
}
// 返回单行单列的通用方法
public Object queryScalar(String sql, Object... parameters){
Connection connection = null;
try {
connection = JdbcUtils.getConnection();
return qr.query(connection, sql, new ScalarHandler<>(), parameters);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(null, null, connection);
}
return null;
}
}
8.2 XxxDao类(继承于BasicDao类)
public class newsDao extends BasicDAO<news> {
// 1.有了 BasicDao的方法
// 2.根据业务需求,可以编写特有的方法
// 修改 news其中一条记录
public int ModifyNews_content(int id,String content){
String sql = "update news set content = ? where id = ?";
int update = super.update(sql, content, id);
return update;
}
// 增加一条新闻
...
}
测试:
public class test{
// 修改新闻 id为 7的新闻内容
public static void test(){
newsDAO n = new newsDAO();
int result = n.ModifyNews_content(7,"新的内容");
System.out.println(result>0 ?"修改成功":"修改不成功");
}
public static void main(String[] args) {
test();
}
}