当前位置: 首页 > news >正文

JDBC的介绍续

四 JDBC的事务支持

4.1 银行转账案例演示

4.4.1 案例分析:

1.需求:一个账号fromAccount向另一个账号toAccount转入money元钱
2.分析:
    - 检查两个账号是否存在,不存在的话,结束转账行为
    - 检查转出账号的里金额是否充足,不充足,结束转账行为,充足的话,进行扣款money元
    - 转入账号进行增加money元

4.4.2 代码实现:

 

package com.jdbc.day01._05Transfer;
/*银行转账业务的演示:两个账号,一个金额。*/import com.jdbc.day01.util.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class BankTransferDemo {public static void main(String[] args) throws Exception {boolean flag = transfer("6225113088436225","6225113088436226",1000);}/**** @param fromAccount 转出* @param toAccount 转入* @param money 转账金额* @return 成功true ,失败false*/public static boolean transfer(String fromAccount,String toAccount,double money){Connection conn=null;try{//第一步:先校验参数是否合理if (money<0){System.out.println("转账金额不能为负数");return false;}if (fromAccount==null || toAccount==null|| fromAccount.trim().length()==0|| toAccount.trim().length()==0){System.out.println("转账账号不能为空");return false;}//验证两个账号是否真实有效,去数据库中验证conn = DBUtil.getConnection();String sql = "select * from bank_account where account_id = ?";//获取预编译对象 先验证转出账号PreparedStatement state = conn.prepareStatement(sql);state.setString(1,fromAccount);ResultSet resultSet = state.executeQuery();if (!resultSet.next()){// 说明转出账号不存在System.out.println("转出账号不存在");return false;}PreparedStatement state2 = conn.prepareStatement(sql);state2.setString(1,toAccount);ResultSet resultSet2 = state2.executeQuery();if (!resultSet2.next()){//说明转入账号不存在System.out.println("转入账号不存在");return false;}//获取转出账号的余额if (resultSet.getDouble("account_balance")<money){//说明转出账号余额不足System.out.println("转出账号余额不足");return false;}//余额充足,可以转出String sql2 = "update bank_account set account_balance=? where account_id=?";PreparedStatement state3 = conn.prepareStatement(sql2);state3.setDouble(1,resultSet.getDouble("account_balance")-money);state3.setString(2,fromAccount);state3.executeUpdate();/*写一个异常,来模拟程序正好执行到这里,银行断电。*/try{String str = null;System.out.println(str.length());}catch (Exception e){//如果出现异常conn.rollback();}/*造成的结果:出账没问题,已经扣除相应金额,但是入账出了问题,没有执行到入职步骤,因此入账失败*/PreparedStatement state4 = conn.prepareStatement(sql2);state4.setDouble(1,resultSet2.getDouble("account_balance")+money);state4.setString(2,toAccount);state4.executeUpdate();return true;}catch (Exception e){e.printStackTrace();try {conn.rollback();} catch (SQLException ex) {e.printStackTrace();}}finally {DBUtil.closeConnection(conn);}DBUtil.closeConnection(conn);return false;}
}

4.2 转账异常演示及事务的引入

        造成的结果:出账没问题,已经扣除相应金额,但是入账出了问题,没有执行到入职步骤,因此入账失败。在数据库层面就是,入账金额没变,但是出账金额少了。这样子是不合理的。程序员不应该让这种事情发生。 因此引入了一个关于数据库的概念: 事务(TCL)。

4.3 JDBC的事务支持

4.3.1 事务的概念:

        当一个业务需求涉及到N个DML操作时,这个业务(或者时N个DML操作)当成一个整体来处理。在处理的过程中,如果有失败或异常,我们要回到业务开始时如果成功处理,我们再将数据持久化到磁盘中。这样一个过程我们称之为一个事务。具有原子性。不可切割。

TCL(事务控制语言):提供了三个关键字,来保证这些特性:
            commit:  提交,进行持久化保存
            rollback: 回滚到事务开始的时候。
            savepoint:  设置保存点,事务在发生过程中临时保存,相当于游戏的存档功能。

什么时候会涉及到事务的概念?
            只有当使用DML语言(insert into、update、delete)时,才会触发事务。
            - 默认情况下,mysql的一个DML语句,就是一个完整的事务,会自动触发commit操作。
            - 如果你的事务是涉及到多个DML操作时,应该取消mysql的默认机制,将你的这多个DML操作,当成一个事务来处理。 

4.3.2 事务的特性:

特点:ACID
        1.原子性: 原子具有不可再切割性,即最小的。(之前物理化学中认为原子是最小的)
        2.一致性: 做这件事之前和之前的数据之和是一样的。
        3.隔离性: 这个事务被一个人做的时候,另外的人需要等待。(类似于线程的同步)
        4.持久性: 这个事务如果完成了,就必须要持久化到磁盘上。

4.3.3 MySQL事务

- 默认情况下,MySQL每执行一条SQL语句,都是一个单独的事务。
- 如果需要在一个事务中包含多条SQL语句,那么需要开启事务和结束事务。
    开启事务start transaction
    结束事务commit或rollback

回滚情况

START TRANSACTION;
UPDATE account SET balance=balance-10000 WHERE id=1;
SELECT * FROM account;
UPDATE account SET balance=balance+10000 WHERE id=2;
ROLLBACK;

提交情况

START TRANSACTION;
UPDATE account SET balance=balance-10000 WHERE id=1;
SELECT * FROM account;
UPDATE account SET balance=balance+10000 WHERE id=2;
COMMIT;

4.3.4 JDBC的事务支持

Connection.setAutoCommit(boolean flag):此方法可以取消事务的自动提交功能,值为false。  
Connection.commit():进行事务提交   。  
Connection.rollback():进行事务回滚。  

4.3.5 多事务的情况:

脏读:事务A读取了事务B刚刚更新的数据,但是事务B回滚了,这样就导致事务A读取的为脏数据,我们称之为脏读。
如公司某财务人员更新公司入账报表时,在DML语句中的数字后少添加了一个0,但是未提交,然后吃饭,吃饭回来,发现错误然后更正后做了提交。而在吃饭期间,老板要求秘书查看一下报表,秘书看到的是少个0的数据。这就是脏读。

不可重复读:事务A读取同一条记录两次,但是在两次之间事务B对该条记录进行了修改并提交,导致事务A两次读取的数据不一致。
它和脏读的区别是,脏读是事务A读取了另一个事务B未提交的脏数据,而不可重复读则是事务A读取了事务B提交的数据,多数情况下,不可重复读并不是问题,因为我们多次查询某个数据时,当然要以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,比如,老板让B和C分别核对事务A操作的数据,结果可能不同,老板是怀疑B呢,还是C呢? 

幻读:事务A在修改全表的数据,比如将字段age全部修改为0岁,在未提交时,事务B向表中插入或删除数据,如插入一条age为25岁的数据。这样导致事务A读取的数据与需要修改的数据不一致,就和幻觉一样。
幻读不可重复读

相同点:都是针对于另外一个已经提交的事务而言。

不同点:不可重复读是针对于同一条记录来说的(delete或update 同一条记录),而幻读是针对于一批数据来说的(insert) 

4.3.6 隔离机制

1、未提交读(read uncommitted): 就是不做隔离控制,可以读到“脏数据”,可能发生不可重复读,也可能出现幻读。 

2、提交读(read committed): 提交读就是不允许读取事务没有提交的数据
显然这种级别可以避免了脏读问题。但是可能发生不可重复读,幻读这个隔离级别是大多数数据库(除了mysql)的默认隔离级别。

3、可重复读 (repeatableread): 为了避免提交读级别不可重复读的问题,在事务中对符合条件的记录上"排他锁",这样其他事务不能对该事务操作的数据进行修改,可避免不可重复读的问题产生。由于只对操作数据进行上锁的操作,所以当其他事务插入或删除数据时,会出现幻读的问题此种隔离级别为MysqI默认的隔离级别。

4、序列化(Serializable),在事务中对表上锁,这样在事务结束前,其他事务都不能够对表数据进行操作(包括新增,删除和修改)
这样避免了脏读,不可重复读和幻读是最安全的隔离级别。但是由于该操作是堵塞的,因此会严重影响性能.

 修改当前会话的隔离机制:

set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
set session transaction isolation level read uncommitted;查询mysql的当前会话的隔离机制:
select @@tx_isolation;

4.4 修改转账代码:改为手动提交

import com.jdbc.day01.util.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class BankTransferDemo {public static void main(String[] args) throws Exception {boolean flag = transfer("6225113088436225","6225113088436226",1000);}/**** @param fromAccount 转出* @param toAccount 转入* @param money 转账金额* @return 成功true ,失败false*/public static boolean transfer(String fromAccount,String toAccount,double money){Connection conn=null;try{//第一步:先校验参数是否合理if (money<0){System.out.println("转账金额不能为负数");return false;}if (fromAccount==null || toAccount==null|| fromAccount.trim().length()==0|| toAccount.trim().length()==0){System.out.println("转账账号不能为空");return false;}//验证两个账号是否真实有效,去数据库中验证conn = DBUtil.getConnection();String sql = "select * from bank_account where account_id = ?";//获取预编译对象 先验证转出账号PreparedStatement state = conn.prepareStatement(sql);state.setString(1,fromAccount);ResultSet resultSet = state.executeQuery();if (!resultSet.next()){// 说明转出账号不存在System.out.println("转出账号不存在");return false;}PreparedStatement state2 = conn.prepareStatement(sql);state2.setString(1,toAccount);ResultSet resultSet2 = state2.executeQuery();if (!resultSet2.next()){//说明转入账号不存在System.out.println("转入账号不存在");return false;}//获取转出账号的余额if (resultSet.getDouble("account_balance")<money){//说明转出账号余额不足System.out.println("转出账号余额不足");return false;}//因为转账涉及到两个Update语句,因此应该将这两个update语句当成一个事务来处理。// 所以,要在第一个update开始之前,取消自动提交操作conn.setAutoCommit(false); //false表示取消//余额充足,可以转出String sql2 = "update bank_account set account_balance=? where account_id=?";PreparedStatement state3 = conn.prepareStatement(sql2);state3.setDouble(1,resultSet.getDouble("account_balance")-money);state3.setString(2,fromAccount);state3.executeUpdate();/*事务的简单理解:就是做一件事,这件事是一个整休,要是做,就做完。要么就认为这件事没有开始,即使做到了一半,也要想办法回到做这件事之前。*/PreparedStatement state4 = conn.prepareStatement(sql2);state4.setDouble(1,resultSet2.getDouble("account_balance")+money);state4.setString(2,toAccount);state4.executeUpdate();// 能只想到此处,说明转账业务能成功完成,那么就应该提交事务conn.commit();return true;}catch (Exception e){e.printStackTrace();try {conn.rollback();} catch (SQLException ex) {e.printStackTrace();}}finally {DBUtil.closeConnection(conn);}DBUtil.closeConnection(conn);return false;}
}

五 数据库连接池技术

5.1 连接池技术简介:

        在与数据库连接过程中,会非常消耗内存,性能大打折扣。如果每次请求都去重新连接数据库。那么,宕机的几率很高。

因此,我们可以使用连接池技术

连接池的工作原理:

        连接池对象在初始化阶段 一次性创建N个连接对象,这些连接对象存储在连接池对象中。当有请求过来时,先从连接池中寻找空闲连接对象并使用,当使用完后,将连接对象归还给连接池,而不是真正意义上断开连接。这样也可以满足成千上万个请求,同时并提高了数据库的性能。

常用的连接池技术

- dbcp      :是apache组织旗下的一个数据库连接池技术产品
- c3p0      :是一个开源的连接池技术
- druid     :是阿里的数据库连接池技术

5.2 dbcp

5.2.1 资源jar包:

commons-dbcp2-2.6.0.jar
commons-pool2-2.4.3.jar
commons-logging.jar

5.2.2 配置文件dbcp.properties

此配置文件请放在src目录下

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai&useTimezone=true
username=root
password=123456
initialSize=5
maxTotal=50
maxIdle=10
minIdle=3
maxWaitMillis=60000

5.2.3 DBUtildbcp类型的编写

import org.apache.commons.dbcp2.BasicDataSource;import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;public class DBCPUtil {private static String driver ;private static String url ;private static String username ;private static String password ;private static int maxTotal ;private static int maxIdle ;private static int minIdle ;private static long maxWaitMillis ;private static int initialSize ;private static BasicDataSource bds;static{try{//先读取配置文件InputStream input = DBCPUtil.class.getClassLoader().getResourceAsStream("dbcp.properties");Properties pro = new Properties();pro.load(input);driver = pro.getProperty("driver");url = pro.getProperty("url");username = pro.getProperty("username");password = pro.getProperty("password");maxTotal = Integer.parseInt(pro.getProperty("maxTotal"));maxIdle = Integer.parseInt(pro.getProperty("maxIdle"));minIdle = Integer.parseInt(pro.getProperty("minIdle"));maxWaitMillis = Long.parseLong(pro.getProperty("maxWaitMillis"));initialSize = Integer.parseInt(pro.getProperty("initialSize"));//给连接池变量初始化bds = new BasicDataSource();//将各种配置传给连接池bds.setDriverClassName(driver);bds.setUrl(url);bds.setUsername(username);bds.setPassword(password);bds.setMaxTotal(maxTotal);bds.setMaxIdle(maxIdle);bds.setMinIdle(minIdle);bds.setMaxWaitMillis(maxWaitMillis);bds.setInitialSize(initialSize);}catch (Exception e){e.printStackTrace();}}public static Connection getConnection(){//从连接池中获取连接对象Connection conn = null;try {conn = bds.getConnection();} catch (SQLException e) {e.printStackTrace();}return conn;}public static void closeConnection(Connection conn){if(conn!= null){try{//此时因为conn这个对象是从连接池中获取的。// 所以,此时的close方法,并没有真正关闭连接,而是归还给连接池。conn.close();}catch (Exception e){e.printStackTrace();}}}
}

5.3 c3p0

5.3.1 资源jar包

c3p0-0.9.5-pre8.jar
mchange-commons-java-0.2.7.jar

5.3.2 配置文件c3p0-config.xml

配置文件请放在src目录下

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config><!-- 默认配置,如果没有指定则使用这个配置 --><default-config><property name="user">root</property><property name="password">123456</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai&amp;useTimezone=true&amp;useSSL=false&amp;allowPublicKeyRetrieval=true</property><property name="driverClass">com.mysql.cj.jdbc.Driver</property><!-- 当连接池中的连接用完时,C3P0一次性创建新连接的数目 --><property name="acquireIncrement">10</property><!-- 连接池中保留的最大连接数 --><property name="maxPoolSize">50</property><!-- 连接池中保留的最小连接数 --><property name="minPoolSize">2</property><!-- 初始化时创建的连接数,应在minPoolSize与maxPoolSize之间取值。默认为3; --><property name="initialPoolSize">5</property><!-- 最大空闲时间,超过空闲时间N秒的连接将被丢弃。为0或负数则永不丢弃。默认为0; --><property name="maxIdleTime">600</property></default-config>
</c3p0-config>

5.3.3 DBUtilc3p0类型的编写

import com.mchange.v2.c3p0.ComboPooledDataSource;import java.sql.Connection;public class C3P0Utile {//提供一个C3o的连接池属性private static ComboPooledDataSource ds;static{//构造器会主动读取src的名字为c3p0-config配置文件ds = new ComboPooledDataSource("c3p0-config.xml");}public static Connection getConnection(){Connection conn=null;try {conn= ds.getConnection();} catch (Exception e) {e.printStackTrace();}return conn;}public static void closeConnection(Connection conn){if(conn!=null){try {conn.close();} catch (Exception e) {e.printStackTrace();}}}public static void main(String[] args) {Connection conn = C3P0Utile.getConnection();System.out.println(conn);C3P0Utile.closeConnection(conn);}
}

5.4 druid

5.4.1 资源jar包

druid-1.1.18.jar

5.4.2 配置文件druid.properties

放在src目录下。注意,前面的key值是固定写法

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai&useTimezone=true
username=root
password=123456
maxActive=20
minIdle=3
initialSize=5
maxWait=60000

5.4.3 DBUtildruid类型的编写

import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;public class DruidUtil {private  static DataSource ds;static{try{InputStream io = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");Properties pro = new Properties();pro.load(io);//提供了一个工厂类,里面提供了一个createDataSource(Properties pro)方法//会自动解析prop里的各种键值对,进行赋值ds= DruidDataSourceFactory.createDataSource(pro);}catch (Exception e){e.printStackTrace();}}public static Connection getConnection(){Connection conn=null;try{conn=ds.getConnection();}catch (Exception e){e.printStackTrace();}return conn;}public static void closeConnection(Connection conn){if(conn!=null){try{conn.close();}catch (Exception e){e.printStackTrace();}}}public static void main(String[] args) {Connection conn = DruidUtil.getConnection();System.out.println(conn);DruidUtil.closeConnection(conn);}
}

六 DAO设计模式

6.1 DAO简介

- DAO数据访问对象(Data Access Object)的简写。
- 建立在数据库与业务层之间,封装所有对数据库的访问操作,我们也可称之为持久层
- 目的: 将数据访问逻辑和业务逻辑分开

 如下图所示

一个DAO设计模式包含以下内容

1. 定义实体类:  通过对象关系映射(ORM)将数据库的表结构映射成java类型;表中的每一条记录映射成类的实例。用于数据的传递。

2. 定义一个接口:在此接口中,定义应用程序对此表的所有访问操作,如增,删,改、查,等方法。

3. 定义接口的实现类:实现接口中的所有抽象方法。

4. 定义一个DAO工厂类型:用于返回接口实例 这样,开发人员只需要使用DAO接口即可,具体逻辑就变得透明了,无需了解内部细节。

扩展:项目的包名命名规则

 规范: com.域名.项目名称.模块名称

com.ssy.jdbc03.util
com.ssy.jdbc03.entity
com.ssy.jdbc03.test
com.ssy.jdbc03.dao
com.ssy.jdbc03.dao.impl
com.ssy.jdbc03.service

6.2 DAO的案例示范

6.2.1 创建项目,导入相关资源

6.2.2 编写工具类DBUtil

记得导入druid.properties文件

import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;public class DruidUtil {private  static DataSource ds;static{try{InputStream io = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");Properties pro = new Properties();pro.load(io);//提供了一个工厂类,里面提供了一个createDataSource(Properties pro)方法//会自动解析prop里的各种键值对,进行赋值ds= DruidDataSourceFactory.createDataSource(pro);}catch (Exception e){e.printStackTrace();}}public static Connection getConnection(){Connection conn=null;try{conn=ds.getConnection();}catch (Exception e){e.printStackTrace();}return conn;}public static void closeConnection(Connection conn){if(conn!=null){try{conn.close();}catch (Exception e){e.printStackTrace();}}}public static void main(String[] args) {Connection conn = DruidUtil.getConnection();System.out.println(conn);DruidUtil.closeConnection(conn);}
}

6.2.3 编写实体类

import java.sql.Date;
import java.util.Objects;/***  根据ORM对象关系映射,为数据库中的emp表设计一个实体类 Employee*  1. 表的字段 --->类的属性*  2. 表的每一行记录 ---> 类的具体实例** 建议: 数据库的数值类型,在java中映射成对应的包装类型*/public class Employee {private Integer empno;private String ename;private String job;private Integer mgr;private Date hiredate;private Double sal;private Double comm;private Integer deptno;public Employee() {};public Employee(Integer empno, String ename, String job, Integer mgr, Date hiredate, Double sal, Double comm, Integer deptno) {this.empno = empno;this.ename = ename;this.job = job;this.mgr = mgr;this.hiredate = hiredate;this.sal = sal;this.comm = comm;this.deptno = deptno;}public Integer getEmpno() {return empno;}public void setEmpno(Integer 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 Integer getMgr() {return mgr;}public void setMgr(Integer mgr) {this.mgr = mgr;}public Date getHiredate() {return hiredate;}public void setHiredate(Date hiredate) {this.hiredate = hiredate;}public Double getSal() {return sal;}public void setSal(Double sal) {this.sal = sal;}public Double getComm() {return comm;}public void setComm(Double comm) {this.comm = comm;}public Integer getDeptno() {return deptno;}public void setDeptno(Integer deptno) {this.deptno = deptno;}@Overridepublic boolean equals(Object object) {if (this == object) return true;if (object == null || getClass() != object.getClass()) return false;Employee employee = (Employee) object;return Objects.equals(empno, employee.empno) && Objects.equals(ename, employee.ename) && Objects.equals(job, employee.job) && Objects.equals(mgr, employee.mgr) && Objects.equals(hiredate, employee.hiredate) && Objects.equals(sal, employee.sal) && Objects.equals(comm, employee.comm) && Objects.equals(deptno, employee.deptno);}@Overridepublic int hashCode() {return Objects.hash(empno, ename, job, mgr, hiredate, sal, comm, deptno);}@Overridepublic String toString() {return "Employee{" +"empno=" + empno +", ename='" + ename + '\'' +", job='" + job + '\'' +", mgr=" + mgr +", hiredate=" + hiredate +", sal=" + sal +", comm=" + comm +", deptno=" + deptno +'}';}
}

6.2.4 定义接口

import com.youcai.emp.vo.Employee;import java.util.List;/***  根据实体类Employee和数据库中的emp表,来设计DAO层的接口类型*  该接口中实际上就是封装了一些与数据库进行交互的方法。*  增,删,改,查*/public interface EmployeeDao {/*** 从java的面相对象思想考虑,前段提供了一个员工的所有的零散信息* 传入服务端后,应该封装到实体类的具体实例里。然后在DAO蹭,我们* 将具体实例保存到数据库中,所以,方法带实体类参数* @param employee*/void addEmployee(Employee employee);/*** 删除某一个员工,一定是前段传入了一个代表该员工的唯一标识。即主键字段** 因此该方法也应该带参数* @param empno*/void deleteEmployee(Integer empno);/*** 修改一个员工的信息,在前段的员工信息的文本框中,不一定是修改了什么信息* 因此,后端就应该考虑全面。认为全都可能被修改了,所以重新封装成对象。* 传入方法** 注意: 形参已经是修改后的数据了。* @param employee*/void updateEmployee(Employee employee);/*** 通过唯一标识,查询一个员工的所有信息,结果封装成实体类对象* @param empno* @return*/Employee findEmployeeById(Integer empno);/*** 查询表中的所有员工信息,封装成集合,不需要形参,因为sql语句不需要: select * from 表名;* 每一条记录都应该封装成实体类对象。* 多个对象,应该存储到集合容器中,所以返回值应该是一个集合** @return*/List<Employee> findAll();/*** 分页查询* select ... from emp order by ... limit (page-1)*pageSize,pageSize*/List<Employee> findByPage(Integer page,Integer pageSize);}

6.2.5 编写实现类

import com.youcai.emp.dao.EmployeeDao;
import com.youcai.emp.util.DruidUtil;
import com.youcai.emp.vo.Employee;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;/*** 定义EmployeeDao接口的实现类型*/public class EmployDaoImpl implements EmployeeDao {@Overridepublic void addEmployee(Employee employee) {Connection conn = null;try{conn= DruidUtil.getConnection();String sql = "insert into emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) values(?,?,?,?,?,?,?,?)";PreparedStatement prep = conn.prepareStatement(sql);prep.setInt(1,employee.getEmpno());prep.setString(2,employee.getEname());prep.setString(3,employee.getJob());prep.setInt(4,employee.getMgr());prep.setDate(5, employee.getHiredate());prep.setDouble(6,employee.getSal());prep.setDouble(7,employee.getComm());prep.setInt(8,employee.getDeptno());prep.executeUpdate();}catch (Exception e){e.printStackTrace();}finally {DruidUtil.closeConnection(conn);}}@Overridepublic void deleteEmployee(Integer empno) {Connection conn =null;try{conn=DruidUtil.getConnection();String sql = "delete from emp where empno=?";PreparedStatement prep = conn.prepareStatement(sql);prep.setInt(1,empno);prep.executeUpdate();}catch (Exception e){e.printStackTrace();}finally {DruidUtil.closeConnection(conn);}}@Overridepublic void updateEmployee(Employee employee) {Connection conn =null;try{conn=DruidUtil.getConnection();//获取预编译语句对象String sql = "update emp set ename=?,job=?,mgr=?,hiredate=?,sal=?,comm=?,deptno=? where empno=?";PreparedStatement prep = conn.prepareStatement(sql);prep.setString(1,employee.getEname());prep.setString(2,employee.getJob());prep.setInt(3,employee.getMgr());prep.setDate(4,new java.sql.Date(employee.getHiredate().getTime()));prep.setDouble(5,employee.getSal());prep.setDouble(6,employee.getComm());prep.setInt(7,employee.getDeptno());prep.setInt(8,employee.getEmpno());prep.executeUpdate();}catch (Exception e){e.printStackTrace();}finally {DruidUtil.closeConnection(conn);}}@Overridepublic Employee findEmployeeById(Integer empno) {Connection conn = null;Employee employee = null;try{conn=DruidUtil.getConnection();String sql = "select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp where empno=?";PreparedStatement prep = conn.prepareStatement(sql);prep.setInt(1,empno);ResultSet res = prep.executeQuery();if(res.next()){employee = new Employee(res.getInt(1),res.getString(2),res.getString("job"),res.getInt("mgr"),res.getDate("hiredate"),res.getDouble("sal"),res.getDouble("comm"),res.getInt("deptno"));}}catch (Exception e){e.printStackTrace();}finally {DruidUtil.closeConnection(conn);}return employee;}@Overridepublic List<Employee> findAll() {List<Employee> employees = new ArrayList<>();Connection conn = null;try{conn=DruidUtil.getConnection();String sql = "select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp";PreparedStatement prep = conn.prepareStatement(sql);ResultSet res = prep.executeQuery();while(res.next()){employees.add(new Employee(res.getInt("empno"),res.getString("ename"),res.getString("job"),res.getInt("mgr"),res.getDate("hiredate"),res.getDouble("sal"),res.getDouble("comm"),res.getInt("deptno")));}}catch (Exception e){e.printStackTrace();}finally {DruidUtil.closeConnection(conn);}return employees;}@Overridepublic List<Employee> findByPage(Integer page, Integer pageSize) {Connection conn = null;List<Employee> employees = new ArrayList<>();try{conn=DruidUtil.getConnection();String sql = "select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp limit ?,?";PreparedStatement prep = conn.prepareStatement(sql);prep.setInt(1,(page-1)*pageSize);prep.setInt(2,pageSize);ResultSet res = prep.executeQuery();while(res.next()){employees.add(new Employee(res.getInt(1),res.getString(2),res.getString("job"),res.getInt("mgr"),res.getDate("hiredate"),res.getDouble("sal"),res.getDouble("comm"),res.getInt("deptno")));}}catch (Exception e){e.printStackTrace();}finally {DruidUtil.closeConnection(conn);}return employees;}
}

6.2.6 编写DAO工厂类

import com.youcai.emp.dao.DeptDao;
import com.youcai.emp.dao.EmployeeDao;
import com.youcai.emp.dao.impl.DeptDaoImpl;
import com.youcai.emp.dao.impl.EmployDaoImpl;/*** 定义一个持久层的工厂类型,* 在该类型中提供一些静态工具方法,用于获取每个实体类对应的DAO接口实例**/public class DaoFactory {private static EmployeeDao employeeDao;private static DeptDao deptDao;//私有化构造器,防止在外部直接new对象private DaoFactory(){}//提供一个共有的静态方法,来返回接口的实例对象public static EmployeeDao getEmployeeDaoInstance(){if (employeeDao == null){employeeDao = new EmployDaoImpl();}return employeeDao;}public static DeptDao getDeptDaoInstance(){if (deptDao == null){deptDao = new DeptDaoImpl();}return deptDao;}}

6.2.7 编写测试类

import com.youcai.emp.dao.EmployeeDao;
import com.youcai.emp.dao.impl.EmployDaoImpl;
import com.youcai.emp.util.DaoFactory;
import com.youcai.emp.vo.Employee;
import org.junit.Test;import java.sql.Date;
import java.util.List;public class employeeDaoTest {@Testpublic void testAddEmployee(){Employee e1 = new Employee(1111,"qpz","hero",3619, Date.valueOf("2024-08-01"), 15000.0,1002.0,20);EmployeeDao dao = DaoFactory.getEmployeeDaoInstance();dao.addEmployee(e1);}@Testpublic void testDeleteEmployee(){EmployeeDao dao = DaoFactory.getEmployeeDaoInstance();dao.deleteEmployee(10000);}/*** 测试修改员工*/@Testpublic void testUpdateEmployee(){// 创建一个员工对象,来模拟已经修改完后并封装的操作Employee e1 = new Employee(10000,"superman","hero",7499, Date.valueOf("2024-08-01"), 10000.0,100.0,10);//调用修改方法,直接提交到数据库//通过工厂类型里的工具方法,获取EmployeeDao实例EmployeeDao dao = DaoFactory.getEmployeeDaoInstance();dao.updateEmployee(e1);}@Testpublic void testFindEmployeeById(){EmployeeDao dao = DaoFactory.getEmployeeDaoInstance();Employee e1 = dao.findEmployeeById(7934);System.out.println(e1);}@Testpublic void testFindAll(){EmployeeDao dao = DaoFactory.getEmployeeDaoInstance();List<Employee> fall = dao.findAll();for (Employee e:fall){System.out.println(e);}}@Testpublic void testFindByPage(){EmployeeDao dao = DaoFactory.getEmployeeDaoInstance();List<Employee> page = dao.findByPage(1, 5);page.forEach(System.out::println);}/*** junit是用来进行测试的。其中有很多注解。* @Test: 用于测试方法,相当于man方法,可以直接运行* @Before: 位于方法上,有该注解的方法,会优先于有@Test注解的方法执行* @After: 位于方法上,有该注解的方法,会在有@Test注解的方法执行完之后执行*/@Testpublic void test1(){System.out.println(1+2);}@Testpublic void test2(){System.out.println(Math.random());}}

注解测试所需的包

hamcrest-core-1.3.jar
junit-4.12.jar

七 dbutils第三方工具类的使用

7.1 简介

- 此工具封装了DAO层(持久层)的逻辑。减少了开发周期。
- jar包:commons-dbutils-1.7.jar
- 常用API: 
1. QueryRunner类型:可以直接使用连接池技术来操作数据库,进行增删改查
   构造器:QueryRunner(DataSource ds)
             返回一个指定数据库连接池得QueryRunner对象
   非静态方法:query(String sql, ResultSetHandler<T> rsh)
            通过sql,及其ReusltSetHandler的子类型来获取数据并封装成相应对象
2. ResultSetHandler:关于结果集的一个接口。
    其实现类如下:
    BeanHandler:将查询到的数据的第一条封装成实体类对象
    BeanListHandler:将查询到的数据的第一条封装成实体类对象的集合 

7.2 代码测试:

public class Testdbutils {@Testpublic 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);}@Testpublic 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);}@Testpublic 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);}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • [数据集][目标检测]电动车头盔佩戴检测数据集VOC+YOLO格式4235张5类别
  • 《深入浅出WPF》读书笔记.11Template机制(上)
  • 如何编写Linux PCI设备驱动器 之一
  • 推荐9个不同风格的音频频谱波形 听音乐怎么能少了它
  • FPGA基础知识
  • 分库分表:应对大数据量挑战的数据库扩展策略
  • Apache Ignite 在处理大规模数据时有哪些优势和局限性?
  • 【第0006页 · 数组】寻找重复数
  • CRIO与Windows下LabVIEW开发对比
  • 数据库的介绍:关系型数据库和非关系型数据库究竟是什么?
  • cmd 常用命令总结
  • 个人网银、手机银行
  • nvidia-smi 随机掉卡,error,禁用GSP功能
  • Day22_K8S
  • 被低估的SQL
  • HTTP那些事
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • 从setTimeout-setInterval看JS线程
  • 给初学者:JavaScript 中数组操作注意点
  • 回流、重绘及其优化
  • 爬虫模拟登陆 SegmentFault
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 我与Jetbrains的这些年
  • 学习JavaScript数据结构与算法 — 树
  • 学习笔记TF060:图像语音结合,看图说话
  • 一道面试题引发的“血案”
  • #{}和${}的区别?
  • #1015 : KMP算法
  • #pragma once
  • #Spring-boot高级
  • (02)vite环境变量配置
  • (2)(2.10) LTM telemetry
  • (2020)Java后端开发----(面试题和笔试题)
  • (9)STL算法之逆转旋转
  • (javaweb)Http协议
  • (STM32笔记)九、RCC时钟树与时钟 第一部分
  • (南京观海微电子)——COF介绍
  • (排序详解之 堆排序)
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • ... 是什么 ?... 有什么用处?
  • .gitignore文件使用
  • .net 程序 换成 java,NET程序员如何转行为J2EE之java基础上(9)
  • .net 获取某一天 在当月是 第几周 函数
  • .net/c# memcached 获取所有缓存键(keys)
  • .Net接口调试与案例
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • .NET值类型变量“活”在哪?
  • /dev/sda2 is mounted; will not make a filesystem here!
  • :class的用法及应用
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
  • @ConditionalOnProperty注解使用说明
  • @NotNull、@NotEmpty 和 @NotBlank 区别
  • @transaction 提交事务_【读源码】剖析TCCTransaction事务提交实现细节
  • @Value获取值和@ConfigurationProperties获取值用法及比较(springboot)