JDBC的介绍续

news2024/11/13 9:47:26

四 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;
    }

    @Override
    public 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);
    }

    @Override
    public int hashCode() {
        return Objects.hash(empno, ename, job, mgr, hiredate, sal, comm, deptno);
    }

    @Override
    public 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 {

    @Override
    public 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);
        }




    }

    @Override
    public 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);
        }

    }

    @Override
    public 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);
        }

    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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 {

    @Test
    public 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);
    }

    @Test
    public void testDeleteEmployee(){
        EmployeeDao dao = DaoFactory.getEmployeeDaoInstance();
        dao.deleteEmployee(10000);
    }


    /**
     * 测试修改员工
     */
    @Test
    public 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);
    }

    @Test
    public void testFindEmployeeById(){
        EmployeeDao dao = DaoFactory.getEmployeeDaoInstance();
        Employee e1 = dao.findEmployeeById(7934);
        System.out.println(e1);
    }

    @Test
    public void testFindAll(){
        EmployeeDao dao = DaoFactory.getEmployeeDaoInstance();
        List<Employee> fall = dao.findAll();
        for (Employee e:fall){
            System.out.println(e);
        }
    }

    @Test
    public 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注解的方法执行完之后执行
     */

    @Test
    public void test1(){
        System.out.println(1+2);
    }

    @Test
    public 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 {
    @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);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2119357.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

N 皇后

题目 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方案。 每一种解法包含一个不同的 n 皇后问题 的棋子放置方案&#xff0c;该方案中 ‘Q’ 和 ‘.’…

RP2040 C SDK 64位定时器功能使用

RP2040 C SDK 64位定时器功能使用 &#x1f9e8;RP2040的64位定时器功能介绍参见&#xff1a;https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#group_hardware_timer &#x1f389;RP2040有一个单64位计数器&#xff0c;每微秒递增一次看起来很复杂&#xf…

[基于 Vue CLI 5 + Vue 3 + Ant Design Vue 3 搭建项目] 02 配置 nodejs 淘宝镜像仓库

文章目录 为什么要配置淘宝镜像仓库呢如何查看镜像仓库如何配置镜像仓库 为什么要配置淘宝镜像仓库呢 主要是因为默认的镜像仓库是国外的&#xff0c;当我们使用 npm 安装依赖的时候会很慢或者失败&#xff0c;我们配置国内的镜像仓库这样就可以加速我们安装镜像的过程&#x…

这些加密软件功能你都知道吗?

1.透明加密与无感操作&#xff1a; 透明加密是许多现代加密软件的核心功能之一&#xff0c;它允许用户在无感知的情况下对文件进行加密和解密。这意味着用户无需改变日常操作习惯&#xff0c;加密和解密过程在后台自动完成&#xff0c;确保了数据的安全性同时不影响工作效率。…

【阿雄不会写代码】全国职业院校技能大赛GZ036第十套

也不说那么多了&#xff0c;要用到这篇博客&#xff0c;肯定也知道他是干嘛的&#xff0c;给博主点点关注点点赞&#xff01;&#xff01;&#xff01;这样博主才能更新更多免费的教程&#xff0c;不然就直接丢付费专栏里了&#xff0c;需要相关文件请私聊

关于OceanBase MySQL 模式中全局索引 global index 的常见问题

在OceanBase的问答区和开源社区钉钉群聊中&#xff0c;时常会有关于全局索引 global index的诸多提问&#xff0c;因此&#xff0c;借这篇博客&#xff0c;针对其中一些普遍出现的问题进行简要的解答。 什么是 global index &#xff1f; 由于 MySQL 不具备 global index 的概…

利用TCP编程实现FTP功能

模拟FTP核心原理&#xff1a;客户端连接服务器后&#xff0c;向服务器发送一个文件。文件名可以通过参数指定&#xff0c;服务器端接收客户端传来的文件&#xff08;文件名随意&#xff09;&#xff0c;如果文件不存在自动创建文件&#xff0c;如果文件存在&#xff0c;那么清空…

uboot源码分析uboot启动流程,uboot-CMD命令调用关系

uboot的最终目的是引导启动内核加载系统&#xff0c;根据这个线索我们可以首先找到uboot引导内核的main函数&#xff0c;查看系统引导的执行跳转的函数 main_loop。 下面对uboot函数的调用关系和主要调用函数进行分析。 一、uboot函数调用关系梳理 函数调用如下&#xff1a; …

Ollama 本地运行大模型(LLM)完全指南

文章介绍了 Ollama 本地运行大模型&#xff08;LLM&#xff09;的方方面面&#xff0c; 包括安装运行、对话、自定义模型、系统提示配置、调试、开发、存储、如何作为服务、OpenAI 的兼容等。 这一年来&#xff0c;我已经习惯了使用线上大模型 API 来工作&#xff0c;只要网络…

2025毕业设计免费指导!!

本人专注于Android/java/PHP/Python/人工智能/数据库/微信小程序技术等领域的开发&#xff0c;以及有好几年的计算机毕业设计方面的实战开发经验和技术积累&#xff1b;尤其是在图像识别、网站开发和微信小程序的开发&#xff0c;很是熟悉和了解&#xff1b;本人也是多年的全栈…

DevOps实现CI/CD实战(六)- Jenkins集成k8s

十、 Jenkins集成k8s Jenkins在集成K8s之前&#xff0c;需要搭建k8s集群&#xff0c;具体搭建步骤&#xff0c;完整笔记 https://github.com/ITenderL/ITenderL.github.io/tree/main/docs/DevOps&#xff0c; 包括完整的DevOps的笔记。 1. 准备部署的yml文件 pipeline.yml …

祝福在茶礼丨酒茶香充满东方古韵特色的中秋礼盒,太惊艳了

中国是礼仪之邦&#xff0c;礼尚往来更是普通不过。象征东方古韵的茶礼成为现代送礼热门&#xff0c;尤其是逢年过节茶礼氛围更是浓郁&#xff0c;跃居礼单榜首。 中秋节作为团圆之节&#xff0c;送礼肯定少不了&#xff01;送茶礼的这几个理由你一定要知道&#xff01; 送茶即…

【动手学深度学习】06 矩阵计算(个人向笔记)

标量导数 这个比较简单&#xff0c;就是直接求导 亚导数 举了两个例子 梯度 下面是当 y 和 x 分别为向量和标量时求导的结果。一个为向量&#xff0c;一个为标量则结果为向量。两个都为向量结果为矩阵&#xff0c;两个都为标量结果为标量 当 y 为标量&#xff0c;x 为列…

关于腾讯IM消息ID不统一的问题?服务端的MsgKey和前端的msgID不一样

角色>前端&#xff1a;web、小程序、客户端&#xff08;ios、安卓&#xff09;&#xff1b;服务端&#xff1b;腾讯IM&#xff1b; 1、背景 IM消息ID不一致&#xff0c;本地没有缓存历史数据&#xff0c;导致在调用腾讯sdk方法时&#xff0c;id不一致报错问题 2、调研目的…

MySQL进阶篇4 - 锁

五、锁 5.1 概述 介绍 分类 5.2 全局锁 介绍 红色代表不可执行的操作&#xff0c;绿色代表可执行的操作 # mysqldump 是 MySQL 提供的数据备份的命令演示 # 如果想进行全库的逻辑备份&#xff0c;那么就需要在逻辑备份之前 # 手动的加上全局锁 flush tables with read …

flask下https教程

一、定义 linux 下flask https 协议 二、实现 linux 下flask https 协议 生成SSL证书和密钥文件。您可以使用工具如openssl来生成自签名SSL证书和密钥文件。运行以下命令生成证书和密钥文件&#xff1a; openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout…

HCIE和CCIE,哪个含金量更高点?

在现在内卷的大环境下&#xff0c;技术岗可谓人人自危&#xff0c;也因此各种认证的重视程度直线升高。 特别是华为认证的HCIE和思科认证的CCIE&#xff0c;它们都代表着网络技术领域的顶尖水平。 但面对这两个高含金量的认证&#xff0c;不得不让人问出这个问题&#xff1a;同…

找到办法了!一个站点,搞定所有访问权限需求

在知识库的构建与管理过程中&#xff0c;如何平衡信息的公开与私密性始终是一大挑战。传统方法往往需要建立多个具有不同访问权限的站点&#xff0c;操作繁琐且难以维护。尤其当企业需同时向公众和内部成员提供知识文章时&#xff0c;这一问题尤为突出。用户频繁反馈&#xff0…

振弦式渗压计安装流程全面指南

在大坝安全监测体系中&#xff0c;振弦式渗压计作为关键设备之一&#xff0c;承担着监测大坝内部渗流压力变化的重任。其安装质量直接关系到监测数据的准确性和大坝安全的评估。因此&#xff0c;制定一套科学、精细的安装流程&#xff0c;对于确保渗压计的正常运行和延长使用寿…

什么是生成式 AI?

人工智能 (AI) 通过使用机器学习与环境交互并执行任务来模仿人类行为&#xff0c;而无需明确指示要输出的内容。 生成式 AI 描述 AI 中用于创建原创内容的一类功能。 人员通常与聊天应用程序中内置的生成式 AI 交互。 此类应用程序的一个常见示例是 Microsoft Copilot&#xf…