Java初学笔记45
十五、DAO 、BasicDao
1. apache-dbutils+ Druid(德鲁伊连接池)简化了JDBC开发,但还有不足
(1)SQL语句是固定,不能通过参数传入,通用性不好,需要进行改进,更方便执行增删改查
(2)对于select 操作,如果有返回值,返回类型不能固定,需要使用泛型
(3)将来的表很多,业务需求复杂,不可能只靠一个Java类完成
2. 引出DAO、BasicDao
(1)DAO:data access object数据访问对象
(2)BasicDao:通用类,是专门和数据库交互的,即完成对数据库(表)的增删改查操作。
3. 代码示例
utils类
package demo.dao_.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* @Package: demo.datasource
* @ClassName: JDBCUtilsByDruid
* @Author: 爱吃凉拌辣芒果
* @CreateTime: 2022/3/9 16:27
* @Description: 基于Druid(德鲁伊)的 JDBCUtils 工具类
*/
public class JDBCUtilsByDruid {
//1. 建立德鲁伊连接池
private static DataSource dataSource;
//2. 加载德鲁伊配置文件
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\\druid.properties"));
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 方法1:获取德鲁伊连接池的“连接”
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
/**方法2:断开与德鲁伊连接池的"连接"
*
* @param resultSet
* @param statement
* @param 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 throwables) {
throwables.printStackTrace();
}
}
}
Student类
package demo.dao_.domain;
import java.util.Date;
/**
* @Package: demo.dao_.domain
* @ClassName: Student
* @Author: 爱吃凉拌辣芒果
* @CreateTime: 2022/3/10 19:07
* @Description:
*/
public class Student {
private int id;
private String name;
private Date birthday;
public Student() {
}
public Student(int id, String name, Date birthday) {
this.id = id;
this.name = name;
this.birthday = birthday;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "\nStudent{" +
"id=" + id +
", name='" + name + '\'' +
", birthday=" + birthday +
'}';
}
}
BasicDAO类
package demo.dao_.dao;
import demo.dao_.utils.JDBCUtilsByDruid;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
* @Package: demo.dao_.dao
* @ClassName: BasicDAO
* @Author: 爱吃凉拌辣芒果
* @CreateTime: 2022/3/10 19:34
* @Description:
*/
public class BasicDAO<T> {
private QueryRunner qu = new QueryRunner();
/**
* 实现对任何表的增加、删除、修改操作
* @param sql sql语句
* @param parameters 传入sql语句的参数
* @return 影响的行数
*/
public int add_de_update(String sql,Object...parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
int update = qu.update(connection, sql, parameters);
return update;
} catch (SQLException throwables) {
throw new RuntimeException(throwables); //将编译异常->运行异常抛出
} finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
/**
* 返回多个对象(即查询的结果是多行), 针对任意表
* @param sql
* @param cla
* @param parameters
* @return
*/
public List<T> Multiple_line(String sql,Class<T> cla,Object...parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
List<T> query = qu.query(connection, sql, new BeanListHandler<>(cla), parameters);
return query;
} catch (SQLException throwables) {
throw new RuntimeException(throwables); //将编译异常->运行异常抛出
} finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
/**
* 返回单个对象(即查询的结果是一行), 针对任意表
* @param sql
* @param cla
* @param parameters
* @return
*/
public T Single_line(String sql,Class<T> cla,Object...parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
T t = qu.query(connection, sql, new BeanHandler<>(cla), parameters);
return t;
} catch (SQLException throwables) {
throw new RuntimeException(throwables); //将编译异常->运行异常抛出
} finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
/**
* 返回单个数据(即查询的结果是某一行某一列), 针对任意表
* @param sql
* @param parameters
* @return
*/
public Object Single_data(String sql,Object...parameters){
Connection connection = null;
try {
connection = JDBCUtilsByDruid.getConnection();
Object o = qu.query(connection, sql, new ScalarHandler(), parameters);
return o;
} catch (SQLException throwables) {
throw new RuntimeException(throwables); //将编译异常->运行异常抛出
} finally {
JDBCUtilsByDruid.close(null,null,connection);
}
}
}
StudentDAO
package demo.dao_.dao;
import demo.dao_.domain.Student;
/**
* @Package: demo.dao_.dao
* @ClassName: StudentDAO
* @Author: 爱吃凉拌辣芒果
* @CreateTime: 2022/3/10 20:14
* @Description: Student独有的操作类
*/
public class StudentDAO extends BasicDAO<Student>{
//1. 继承 BasicDAO 的通用方法
//2. 根据业务需求,可以编写特有的方法.
}
test类
package demo.dao_.test;
import demo.dao_.dao.StudentDAO;
import demo.dao_.domain.Student;
import org.junit.jupiter.api.Test;
import java.util.List;
/**
* @Package: demo.dao_.test
* @ClassName: Testdao
* @Author: 爱吃凉拌辣芒果
* @CreateTime: 2022/3/10 20:15
* @Description: 测试 StudentDAO 对 Student表的操作
*/
public class Testdao {
@Test
public void test(){
StudentDAO studentDAO = new StudentDAO();
//1. 查询多条记录
String sql = "select * from student where id > ?";
List<Student> list = studentDAO.Multiple_line(sql, Student.class, 0);
for (Student student : list) {
System.out.println(student);
}
//2. 查询一条记录
String sql2 = "select * from student where id = ?";
Student student = studentDAO.Single_line(sql2, Student.class, 2);
System.out.println(student);
//3. 查询单个记录
String sql3 = "select birthday from student where id = ?";
Object object = studentDAO.Single_data(sql3, 1);
System.out.println(object);
//4.增加一条数据
String sql4 = "insert into student values(null,?,?)";
int i = studentDAO.add_de_update(sql4, "爱吃凉拌辣芒果", "2000-01-01");
System.out.println( i > 0 ? "执行成功" : "执行失败");
}
}
数据库总结
总结:
当高并发时,多个程序访问数据库请求直接与数据库连接,会造成访问数据库速度变慢,甚至出现数据库奔溃的情况,那么我们引入连接池作为缓冲,用它来控制与数据库的通断,此时用户程序只是从这个连接池获得连接或者释放连接,实际上与数据库连接的是连接池。举个例子就像大群人进机场,总是有个缓冲的地方。
常用的连接池有C3P0,Druid,DBCP等,其中C3PO数据库连接池,速度相对较慢,稳定性不错;Druid(德鲁伊) 是阿里提供的数据库连接池,集DBCP、C3PO、Proxool优点于一身的数据库连接池。我们选择使用Druid连接池,并且自己建立一个基于Druid的工具类,JDBCUtilsByDruid,用它的getConnection(),close(result,statement,connection)方法,分别获取连接和释放连接。并在druid.properties中配置好与数据库连接的用户名、密码、数据库名等信息。
连接的问题解决了,那么来到数据处理部分,在我们select查询数据时,必须在结果集result关闭之前操作,否则一旦结果集提前关闭了,select操作无效。此外,只有保持连接,才能进一步对查询的数据进行操作。此时我们想到,如果能把查询到数据存到一个集合中,这样即使断开连接,这些数据还可以进一步操作。所以我们使用Apache提供的一个开源JDBC工具类库,在它的DBUtils包下,使用QueryRunner类中的query方法,update方法,分别来进行查询和增删改的操作,注意查询的时候分为某行某列的单数据查询用ScalarHandler,多数据查询用BeanListHandler类。与此同时,我们需要将要操作的表,建立一个对应的java类,这个类根据表的属性设置对应的私有属性,以及构造函数,toString方法,getter和setter方法。这个类我们称作javabean/domain/pojo。
利用Druid+Apache-DBUtils,连接和数据处理的问题解决了,但是有个新的问题,就是sql语句过硬,无法写入参数,已经固定写死。如果一个数据库操作的表过多,对于select 操作,如果有返回值,返回类型(domain)不能固定,需要使用泛型。
所以我们把之前的过程模块化,把其中的:连接部分,sql增、删、改、查通用部分,断开连接部分。写成BasicDao类且为泛型,因为这是公共的部分。对于每一个表,分别建立对应的类,我们称为domain。接着,我们分别对每个表的操作,写成一个独有的DAO类,并且继承BasicDao类。
分别建立四个包包:domain,dao,utils,test来存放domain的类,dao的类,JDBCUtilsByDruid工具类,测试类。