10. DAO设计模式
10.1 DAO简介
-dao是数据访问对象(Data Access object)的简写。
-是为应用程序的持久层所设计的编程模式。
-持久层指的是数据库与业务层之间的逻辑,即与数据库直接交互的逻辑层,封装了所有对数据库的访问
-目的:将数据访问逻辑和业务逻辑分开。
一个DAO设计模式包含以下内容
1. 定义实体类:
通过对象关系映射(ORM)将数据库的表结构映射成java类型;表中的每一条记录映射成类的实例。用于数
据的传
递。
2. 定义一个接口:
在此接口中,定义应用程序对此表的所有访问操作,如增,删,改、査,等方法。
3. 定义接口的实现类
实现接口中的所有抽象方法。
4. 定义一个DAO工厂类型
用于返回接口实例
这样,开发人员只需要使用DAO接口即可,具体逻辑就变得透明了,无需了解内部细节
扩展:项目的包名命名规则
com.qianfeng.jdbcO3.util
com.qianfeng.jdbcO3.entity
com.qianfeng.jdbcO3.test
com.qianfeng.jdbcO3.dao
com.qianfeng.jdbcO3.dao.imp!
com.qianfeng.jdbcO3.service
10.2 DAO的案例示范
10.2.1创建项目,导入相关资源
10.2.2编写工具类DBUtil
10.2.3编写实体类
import java.sql.Date;
import java.util.Objects;
/**
\* 以orm关系将数据库中的emp表映射成java中的Emp类型
\* 表的字段映射成类的属性
*/
public class Emp {
private int empno;
private String ename;
private String job;
private int mgr;
private Date hiredate;
private double salary;
private double comm;
private int deptno;
public Emp(){}
public Emp(int empno, String ename, String job, int mgr, Date hiredate,
double salary, double comm, int deptno) {
this.empno = empno;
this.ename = ename;
this.job = job;
this.mgr = mgr;
this.hiredate = hiredate;
this.salary = salary;
this.comm = comm;
this.deptno = deptno;
}
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public int getMgr() {
return mgr;
}
public void setMgr(int mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getComm() {
return comm;
}
public void setComm(double comm) {
this.comm = comm;
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Emp emp = (Emp) o;
return empno == emp.empno &&
mgr == emp.mgr &&
Double.compare(emp.salary, salary) == 0 &&
Double.compare(emp.comm, comm) == 0 &&
deptno == emp.deptno &&
Objects.equals(ename, emp.ename) &&
Objects.equals(job, emp.job) &&
Objects.equals(hiredate, emp.hiredate);
}
@Override
public int hashCode() {
return Objects.hash(empno, ename, job, mgr, hiredate, salary, comm,
deptno);
}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", mgr=" + mgr +
", hiredate=" + hiredate +
", salary=" + salary +
", comm=" + comm +
", deptno=" + deptno +
'}';
}
}
10.2.4 定义接口
import com.qianfeng.jdbc03.entity.Emp;
import java.util.List;
/**
\* 设计针对于实体类Emp和数据库里的emp表设计对数据库操作的接口
\* 提供相应操作的抽象方法
*/
public interface EmpDao {
/**
\* 提供向数据库中插入数据的方法,
\* @param e 面向对象思想可以使用实体类的实例
*/
void addEmp(Emp e);
/**
\* 提供删除数据库内的一条记录方法,通过id进行删除
\* @param empno 数据库表中的主键
*/
void deleteById(int empno);
/**
\* 修改方法。
\* @param e 传入前先设置成要修改的数据,然后传入方法中进行update语句赋值
*/
void modifyEmp(Emp e);
/**
\* 通过唯一键查询一条记录
\* @param empno
\* @return 封装成实体类实例
*/
Emp findById(int empno);
/**
\* 查询所有的记录。
\* @return 封装成类的实例,并存入集合
*/
List<Emp> findAll();
/**
\* 分页查询
\* @param page 要查询的页数
\* @param pageSize 每页显示的条数
\* @return 一页的所有记录,封装到集合中
*/
List<Emp> findByPage(int page,int pageSize);
}
10.2.5 编写实现类
public class EmpDaoImpl implements EmpDao {
@Override
public void addEmp(Emp e) {
Connection conn = null;
PreparedStatement ps = null;
try{
conn = DBUtil.getConnection();
String sql = "insert into emp values (?,?,?,?,?,?,?,?)";
ps = conn.prepareStatement(sql);
ps.setInt(1,e.getEmpno());
ps.setString(2,e.getEname());
ps.setString(3,e.getJob());
ps.setInt(4,e.getMgr());
ps.setDate(5,e.getHiredate());
ps.setDouble(6,e.getSalary());
ps.setDouble(7,e.getComm());
ps.setInt(8,e.getDeptno());
ps.executeUpdate();
}catch (Exception e1){
e1.printStackTrace();
}finally{
DBUtil.closeConnection(conn,ps,null);
}
}
@Override
public void deleteById(int empno) {
}
@Override
public void modifyEmp(Emp e) {
}
@Override
public Emp findById(int empno) {
return null;
}
@Override
public List<Emp> findAll() {
return null;
}
@Override
public List<Emp> findByPage(int page, int pageSize) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List<Emp> emps = new ArrayList<Emp>();
try{
conn = DBUtil.getConnection();
String sql = "select * from emp order by empno limit ?,?";
ps = conn.prepareStatement(sql);
ps.setInt(1,(page-1)*pageSize);
ps.setInt(2,pageSize);
rs = ps.executeQuery();
Emp e = null;
while(rs.next()){
int empno = rs.getInt(1);
String ename = rs.getString(2);
String job = rs.getString("job");
int mgr = rs.getInt("mgr");
Date hiredate = rs.getDate("hiredate");
double salary = rs.getDouble("sal");
double comm = rs.getDouble("comm");
int deptno = rs.getInt("deptno");
e = new Emp(empno,ename,job,mgr,hiredate,salary,comm,deptno);
emps.add(e);
}
}catch (Exception e1){
e1.printStackTrace();
}finally{
DBUtil.closeConnection(conn,ps,rs);
}
return emps;
}
}
10.2.6 编写DAO工厂类(重点)
public class DaoFactory{
//定义属性EmpDao属性
private static EmpDao empdao = new EmpDaoImpl();
//让构造函数为 private,这样该类就不会被实例化
private DaoFactory(){}
public synchronized static EmpDao getInstance(){
return empdao;
}
}
10.2.7 编写测试类
public class TestDao {
@Test
public void testAddEmp(){
EmpDao dao = DaoFactory.getInstance();
Emp e = new Emp(9007,"huanghua","manager",7369,
Date.valueOf("2019-1-1"),3000.0,200.0,20);
dao.addEmp(e);
}
@Test
public void testFindByPage(){
EmpDao dao = DaoFactory.getInstance();
List<Emp> emps = dao.findByPage(3,5);
for(Emp e:emps){
System.out.println(e);
}
}
}
11. dbutils第三方工具类的使用(重点)
11.1 简介
此工具封装了DAO层(持久层)的逻辑。减少了开发周期。
-jar包:commons-dbutils-1.7.jar
-常用API:
1. QueryRunner类型:可以直接使用连接池技术来操作数据库,进行增删改查
构造器:
QueryRunner()
创建一个连接对象,但连接的数据库需要在之后的操作中写出来
QueryRunner(DataSource ds)
返回一个指定数据库连接池得QueryRunner对象
非静态方法:
执行query操作
query(string sql, ResultSetHandler<T> rsh)
query(string sql, ResultSetHandler<T> rsh,筛选条件)
执行insert update delete 操作
update(String sql,Object...params)
2. 通过sqL及其ReusltSetHandler的子类型来获取数据并封装成相应对象
ResultsetHandler:关于结果集的一个接口。
其实现类如下:
MapHander:单行处理器,结果转成Map<String,Object>列名为键。
MapListHander:多行处理器,结果转成List<Map<String,Object>>一般主键为键,未定义的则第一个字段为键。
BeanHandler:单行处理器,将査询到的数据的第一条封装成实体类对象,例: new BeanHandler<>(Employee.class)
BeanListHandler:多行处理器,将査询到的数据的第一条封装成实体类对象的集合
ColumnListHander:多行单列处理器,结果转成List<Object>,例: new ColumnListHander("name")
ScalarHandler:单行单列处理器,把结果集转成Object,一般用于聚类查询,例:select count(*) from emp;
ArrayListHandler:用于将查询到的记录都封装到Object数组中,并存入到List集合中,例 new ArrayListHandler()
MapHandler:用于将查询到的第一条记录封装成Map对象,字段名作为键组,字段名作为值组。
MapListHandler:用于将查询到的每一条记录封装成Map对象,字段名作为键组,字段名作为值组。
11.2代码测试
public class Testdbutils {
@Test
public void testFindOne() throws SQLException {
QueryRunner qr = new QueryRunner(DBUtil.getPool());
Emp emp = qr.query("select * from emp",new BeanHandler<Emp>(Emp.class));
System.out.println(emp);
}
@Test
public void testFindOneParam() throws SQLException {
QueryRunner qr = new QueryRunner(DBUtil.getPool());
Emp emp = qr.query("select * from emp where empno =?",
new BeanHandler<Emp>(Emp.class),9007);
System.out.println(emp);
}
@Test
public void testFindAll() throws SQLException {
QueryRunner qr = new QueryRunner(DBUtil.getPool());
List<Emp> emp = qr.query("select * from emp",
new BeanListHandler<Emp>(Emp.class));
System.out.println(emp);
}
}
12. JDBC的批处理
12.1 概念
每一次的sql操作都会占用数据库的资源。如果将N条操作先存储到缓存区中,然后再一次性刷到数据库中,这 就减少了与数据库的交互次数。因此可以提高效率。
12.2 Statement
批处理中不能再出现execute方法
addBatch(String sql):将sql语句添加到缓存中
executeBatch():将缓存中的sql一次性刷到数据库中
12.3 代码
@Test
public void testBatch(){
Connection conn = null;
Statement stat = null;
try{
conn = DBUtil.getConnection();
stat = conn.createStatement();
int num = 0;
while(num<1003){
String sql = "insert into testbatch values (null,'zs"+num+"','f')";
stat.addBatch(sql);//将sql语句添加到缓存中,
if(num%50==0){
stat.executeBatch();//缓存中每有50条都刷新一次。
}
num++;
}
stat.executeBatch();//循环结束后,将缓存中剩余的不足50条的全都刷新出去
}catch (Exception e){
e.printStackTrace();
}finally {
DBUtil.closeConnection(conn,stat,null);
}
}