dbutils的使用详解_使用动态代理实现事务控制及Dbutils详解

案例结构

8f555aba822f546f5b2e428e5357346e.png

pom.xml

<packaging>jarpackaging>    <dependencies>        <dependency>            <groupId>org.springframeworkgroupId>            <artifactId>spring-contextartifactId>            <version>5.0.2.RELEASEversion>        dependency>        <dependency>            <groupId>org.springframeworkgroupId>            <artifactId>spring-testartifactId>            <version>5.0.2.RELEASEversion>        dependency>        <dependency>            <groupId>commons-dbutilsgroupId>            <artifactId>commons-dbutilsartifactId>            <version>1.4version>        dependency>        <dependency>            <groupId>mysqlgroupId>            <artifactId>mysql-connector-javaartifactId>            <version>8.0.15version>        dependency>        <dependency>            <groupId>c3p0groupId>            <artifactId>c3p0artifactId>            <version>0.9.1.2version>        dependency>        <dependency>            <groupId>junitgroupId>            <artifactId>junitartifactId>            <version>4.12version>        dependency>    dependencies>    

Account实体类

import java.io.Serializable;/** * 账户的实体类 */public class Account implements Serializable {    private Integer id;    private String name;    private Float money;    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Float getMoney() {        return money;    }    public void setMoney(Float money) {        this.money = money;    }    @Override    public String toString() {        return "Account{" +                "id=" + id +                ", name='" + name + '\'' +                ", money=" + money +                '}';    }}
import com.itheima.domain.Account;import java.util.List;/** * 账户的持久层接口 */public interface IAccountDao {    /**     * 查询所有     * @return     */    ListfindAllAccount();    /**     * 查询一个     * @return     */    Account findAccountById(Integer accountId);    /**     * 保存     * @param account     */    void saveAccount(Account account);    /**     * 更新     * @param account     */    void updateAccount(Account account);    /**     * 删除     * @param accountId     */    void deleteAccount(Integer accountId);    /**     * 根据名称查询账户     * @param accountName     * @return      如果有唯一的一个结果就返回,如果没有结果就返回null     *              如果结果集超过一个就抛出异常     */    Account findAccountByName(String accountName);}
/** * 账户的持久层实现类 */public class AccountDaoImpl implements IAccountDao {    private QueryRunner runner;    private ConnectionUtils connectionUtils;    public void setConnectionUtils(ConnectionUtils connectionUtils) {        this.connectionUtils = connectionUtils;    }    public void setRunner(QueryRunner runner) {        this.runner = runner;    }    public ListfindAllAccount() {        try {            return runner.query(connectionUtils.getThreadConnection(),"select * from account", new BeanListHandler(Account.class));        }catch (Exception e){            throw new RuntimeException(e);        }    }    public Account findAccountById(Integer accountId) {        try {            return runner.query(connectionUtils.getThreadConnection(),"select * from account where id = ? ", new BeanHandler(Account.class),accountId);        }catch (Exception e){            throw new RuntimeException(e);        }    }    public void saveAccount(Account account) {        try {             runner.update(connectionUtils.getThreadConnection(),"insert into account(name,money)values(?,?)", account.getName(),account.getMoney());        }catch (Exception e){            throw new RuntimeException(e);        }    }    public void updateAccount(Account account) {        try {            runner.update(connectionUtils.getThreadConnection(),"update account set name=?,money=? where id=?", account.getName(),account.getMoney(),account.getId());        }catch (Exception e){            throw new RuntimeException(e);        }    }    public void deleteAccount(Integer accountId) {        try {            runner.update(connectionUtils.getThreadConnection(),"delete from account where id =?",accountId);        }catch (Exception e){            throw new RuntimeException(e);        }    }    public Account findAccountByName(String accountName) {        try {           List accounts = runner.query(connectionUtils.getThreadConnection(),"select * from account where name = ? ", new BeanListHandler(Account.class),accountName);            if(accounts == null || accounts.size() == 0){                return null;            }            if(accounts.size() > 1){                throw new RuntimeException("结果集不唯一,数据有问题");            }            return accounts.get(0);        }catch (Exception e){            throw new RuntimeException(e);    }}}

补充内容:Dbutils的使用和详解

DbUtils 是一个jdbc的工具,使用的范围内非常广,主要是为了简化jdbc的代码。核心类:

  • QueryRunner

  • ResultSetHandler(是一个接口,主要是完成ORM映射,把结果转化成我们需要的java对象)

核心方法:

  • update();用来执行DDL(DDL:create alert,drop;);

  • query();用来执行DML(DML:insert update delete;);

  • batch(); 用来执行批处理;

调用本方法之前,需要先创建对象,代码如下

QueryRunner runner = new QueryRunner(JDBCUtils.getDataSource());

当使用的是无参的构造器时,可以不提供连接池对象,但是在接下来的调用方法是,必须为方法提供Connection对象。

QueryRunner类:

该类简单化了 SQL 查询,它与 ResultSetHandler(接口 后面将会介绍) 组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。

构造函数:

  • QueryRunner() 

  • QueryRunner(Datasource ds)

A:query(Connectionconn, String sql, Object[] params, ResultSetHandler rsh)方法:这一方法执行一个带参数的选择查询,在这个查询中,对象阵列的值被用来作为查询的置换参数。这一方法内在地处理 PreparedStatement 和ResultSet 的创建和关闭。ResultSetHandler对象把从 ResultSet得来的数据转变成一个更容易的或是应用程序特定的格式来使用。

B:query(Stringsql, Object[] params, ResultSetHandler rsh)方法:这几乎与第一种方法一样;唯一的不同在于它不将数据库连接提供给方法,并且它是从提供给构造器的数据源(DataSource) 或使用的setDataSource 方法中重新获得的。

C:query(Connectionconn, String sql, ResultSetHandler rsh)方法:执行一个带参数的选择查询。

D:update(Connectionconn, String sql, Object[] params)方法:这一方法被用来执行一个带参数的插入、更新或删除操作。对象阵列为声明保存着置换参数。

E:update(Stringsql, Object[] params)方法: 这几乎与上一种种方法一样;唯一的不同在于它不将数据库连接提供给方法,并且它是从提供给构造器的数据源(DataSource) 或使用的setDataSource 方法中重新获得的。

F:update(Connectionconn, String sql)方法:执行一个带参数的插入、更新或删除操作。

ResultSetHandler接口:

正如它的名字所示,这一接口执行处理一个java.sql.ResultSet,将数据转变并处理为任何一种形式,这样有益于其应用而且使用起来更容易。这一组件提供了:ArrayHandler :将ResultSet中第一行的数据转化成对象数组。ArrayListHandler:将ResultSet中所有的数据转化成List,List中存放的是Object[]。BeanHandler :将ResultSet中第一行的数据转化成类对象。BeanListHandler :将ResultSet中所有的数据转化成List,List中存放的是类对象。ColumnListHandler :将ResultSet中某一列的数据存成List,List中存放的是Object对象。KeyedHandler :将ResultSet中存成映射,key为某一列对应为Map。Map中存放的是数据。MapHandler :将ResultSet中第一行的数据存成Map映射MapListHandler :将ResultSet中所有的数据存成List。List中存放的是Map。ScalarHandler :将ResultSet中一条记录的其中某一列的数据存成Object等转化类。ResultSetHandler接口提供了一个单独的方法:Object handle(java.sql.ResultSet .rs)。因此任何ResultSetHandler 的执行需要一个结果集(ResultSet)作为参数传入,然后才能处理这个结果集,再返回一个对象。因为返回类型是java.lang.Object,所以除了不能返回一个原始的Java类型之外,其它的返回类型并没有什么限制。如果你发现这七个执行程序中没有任何一个提供了你想要的服务,你可以自己写执行程序并使用它。


import com.itheima.domain.Account;import java.util.List;/** * 账户的业务层接口 */public interface IAccountService {    /**     * 查询所有     * @return     */    ListfindAllAccount();    /**     * 查询一个     * @return     */    Account findAccountById(Integer accountId);    /**     * 保存     * @param account     */    void saveAccount(Account account);    /**     * 更新     * @param account     */    void updateAccount(Account account);    /**     * 删除     * @param accountId     */    void deleteAccount(Integer accountId);    /**     * 转账     * @param sourceName        转出账户名称     * @param targetName        转入账户名称     * @param money             转账金额     */    void transfer(String sourceName,String targetName,Float money);}
事务控制应该都在业务层
/** * 账户的业务层实现类 * * 事务控制应该都是在业务层 */public class AccountServiceImpl implements IAccountService {    private IAccountDao accountDao;    public void setAccountDao(IAccountDao accountDao) {        this.accountDao = accountDao;    }    public List findAllAccount() {       return accountDao.findAllAccount();    }    public Account findAccountById(Integer accountId) {       return accountDao.findAccountById(accountId);    }    public void saveAccount(Account account) {       accountDao.saveAccount(account);    }    public void updateAccount(Account account) {        accountDao.updateAccount(account);    }    public void deleteAccount(Integer accountId) {        accountDao.deleteAccount(accountId);    }    public void transfer(String sourceName, String targetName, Float money) {        System.out.println("transfer.....");        //1.根据名称查询转出账户        Account source = accountDao.findAccountByName(sourceName);        //2.根据名称查询转入账户        Account target = accountDao.findAccountByName(targetName);        //3.转出账户减钱        source.setMoney(source.getMoney()-money);        //4.转入账户加钱        target.setMoney(target.getMoney()+money);        //5.更新转出账户        accountDao.updateAccount(source);        int i = 1/0;  //不满足事务的一致性,减钱的事务提交了,加钱的事务没有提交成功        //6.更新转入账户        accountDao.updateAccount(target);    }}

分析transfer()方法中的问题

int i = 1/0;  //不满足事务的一致性,减钱的事务提交了,加钱的事务没有提交成功

404c04669f8eab7d5e61178f4749040b.png

解释:

代码中一共获取四个连接,前三个事务都成功了所以都提交了,但第四个事务前发生了除零异常,所以没有提交。所以我们解决以上问题的思路就是:将transfer中的操作全放到一个连接中,所有操作要么全成功,要么全失败。方法:使用ThreadLocal对象把Connection和当前线程绑定,从而使一个线程中只有一个能控制事务的对象。注意:事务的控制应该都是在业务层。

ConnectionUtils:

import javax.sql.DataSource;import java.sql.Connection;/** * 连接的工具类,它用于从数据源中获取一个连接,并且实现和线程的绑定 */public class ConnectionUtils {    private ThreadLocal tl = new ThreadLocal();    private DataSource dataSource;    public void setDataSource(DataSource dataSource) {        this.dataSource = dataSource;    }    /**     * 获取当前线程上的连接     * @return     */    public Connection getThreadConnection(){      /*  //1.先从ThreadLocal上获取        Connection conn = tl.get();*/        try {            //1.先从ThreadLocal上获取            Connection conn = tl.get();            //2.判断当前线程上是否有连接            if (conn == null) {                //3.从数据源中获取一个连接,并且存入ThreadLocal中                conn = dataSource.getConnection();                tl.set(conn);            }            //4.返回当前线程上的连接            return conn;        }catch (Exception e){            throw new RuntimeException(e);        }    }    /**     * 将用完的线程与连接解绑     * 把连接和线程解绑     */    public void removeConnection(){        tl.remove();    }}

TransactionManager和事务相关的工具类:

/** * 和事务管理相关的工具类,它包含了:开启事务、提交事务、回滚事务和释放连接 */public class TransactionManager {    private ConnectionUtils connectionUtils;    public void setConnectionUtils(ConnectionUtils connectionUtils) {        this.connectionUtils = connectionUtils;    }    /**     * 开启事务     */    public void beginTransaction(){        try {            connectionUtils.getThreadConnection().setAutoCommit(false);        }catch (Exception e){            e.printStackTrace();        }    }    /**     * 提交事务     */    public void commit(){        try {            connectionUtils.getThreadConnection().commit();        }catch (Exception e){            e.printStackTrace();        }    }    /**     * 回滚事务     */    public void rollback(){        try {            connectionUtils.getThreadConnection().rollback();        }catch (Exception e){            e.printStackTrace();        }    }    /**     * 释放连接     */    public void release(){        try {            connectionUtils.getThreadConnection().close();//还回连接池中            connectionUtils.removeConnection();        }catch (Exception e){            e.printStackTrace();        }    }}

改进后的业务层实现类AccountServiceImpl_OLD:

/** * 账户的业务层实现类 * * 事务控制应该都是在业务层 */public class AccountServiceImpl_OLD implements IAccountService {    private IAccountDao accountDao;    private TransactionManager txManager;    public void setTxManager(TransactionManager txManager) {        this.txManager = txManager;    }    public void setAccountDao(IAccountDao accountDao) {        this.accountDao = accountDao;    }    public ListfindAllAccount() {        try{            //1.开启事务            txManager.beginTransaction();            //2.执行操作            List accounts = accountDao.findAllAccount();            //3.提交事务            txManager.commit();            //4.返回结果            return accounts;        }catch (Exception e){            //5.回滚操作            txManager.rollback();            throw new RuntimeException(e);  //若产生异常,程序不再执行        }finally {            //6.释放连接            txManager.release();        }      //  return accountDao.findAllAccount();    }    public Account findAccountById(Integer accountId) {        try{            //1.开启事务            txManager.beginTransaction();            //2.执行操作            Account account = accountDao.findAccountById(accountId);            //3.提交事务            txManager.commit();            //4.返回结果            return account;        }catch (Exception e){            //5.回滚操作            txManager.rollback();            throw new RuntimeException(e);        }finally {            //6.释放连接            txManager.release();        }      //  return accountDao.findAccountById(accountId);    }    public void saveAccount(Account account) {        try{            //1.开启事务            txManager.beginTransaction();            //2.执行操作            accountDao.saveAccount(account);            //3.提交事务            txManager.commit();        }catch (Exception e){            //4.回滚操作            txManager.rollback();        }finally {            //5.释放连接            txManager.release();        }       // accountDao.saveAccount(account);    }    public void updateAccount(Account account) {        try{            //1.开启事务            txManager.beginTransaction();            //2.执行操作            accountDao.updateAccount(account);            //3.提交事务            txManager.commit();            //4.返回结果        }catch (Exception e){            //5.回滚操作            txManager.rollback();        }finally {            //6.释放连接            txManager.release();        }        //accountDao.updateAccount(account);    }    public void deleteAccount(Integer accountId) {        try{            //1.开启事务            txManager.beginTransaction();            //2.执行操作            accountDao.deleteAccount(accountId);            //3.提交事务            txManager.commit();        }catch (Exception e){            //4.回滚操作            txManager.rollback();        }finally {            //5.释放连接            txManager.release();        }        //accountDao.deleteAccount(accountId);    }    public void transfer(String sourceName, String targetName, Float money) {        try{            //1.开启事务            txManager.beginTransaction();            //2.执行操作            //2.1根据名称查询转出账户            Account source = accountDao.findAccountByName(sourceName);            //2.2根据名称查询转入账户            Account target = accountDao.findAccountByName(targetName);            //2.3转出账户减钱            source.setMoney(source.getMoney()-money);            //2.4转入账户加钱            target.setMoney(target.getMoney()+money);            //2.5更新转出账户            accountDao.updateAccount(source);           // int i = 1/0;  加异常测试事务是否回回滚            //2.6更新转入账户            accountDao.updateAccount(target);            //3.提交事务            txManager.commit();        }catch (Exception e){            //4.回滚操作            txManager.rollback();            e.printStackTrace();        }finally {            //5.释放连接            txManager.release();        }    }}

创建Service代理工厂的对象BeanFactory

/** * 用于创建Service的代理对象的工厂 */public class BeanFactory {    private IAccountService accountService;    private TransactionManager txManager;    public void setTxManager(TransactionManager txManager) {        this.txManager = txManager;    }    public final void setAccountService(IAccountService accountService) {        this.accountService = accountService;    }    /**     * 获取Service代理对象     *     * @return     */    public IAccountService getAccountService() {        return (IAccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(),                accountService.getClass().getInterfaces(),                new InvocationHandler() {                    /**                     * 添加事务的支持                     * @param proxy                     * @param method                     * @param args                     * @return                     * @throws Throwable                     */                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                        if("test".equals(method.getName())){                            return method.invoke(accountService,args);                        }                        Object rtValue = null;                        try {                            //1.开启事务                            txManager.beginTransaction();                            //2.执行操作                            rtValue = method.invoke(accountService, args);                            //3.提交事务                            txManager.commit();                            //4.返回结果                            return rtValue;                        } catch (Exception e) {                            //5.回滚操作                            txManager.rollback();                            throw new RuntimeException(e);                        } finally {                            //6.释放连接                            txManager.release();                        }                    }                });    }}

0e14f1a45d512d40fe02f8bf012e62e4.png

bean.xml

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd">    <bean id="proxyAccountService" factory-bean="beanfactory" factory-method="getAccountService">bean>    <bean id="beanfactory" class="com.itheima.factory.BeanFactory">        <property name="accountService" ref="accountService">property>        <property name="txManager" ref="txManager">property>    bean>    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">      <property name="accountDao" ref="accountDao">property>        <property name="txManager" ref="txManager">property>    bean>    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">        <property name="runner" ref="runner">property>        <property name="connectionUtils" ref="connectionUtils">property>    bean>    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">             bean>    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">        <property name="driverClass" value="com.mysql.cj.jdbc.Driver">property>        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy_spring?serverTimezone=UTC">property>        <property name="user" value="root">property>        <property name="password" value="qy914924771314b">property>    bean>    <bean id="connectionUtils" class="com.itheima.utils.ConnectionUtils">        <property name="dataSource" ref="dataSource">property>    bean>    <bean id="txManager" class="com.itheima.utils.TransactionManager">                <property name="connectionUtils" ref="connectionUtils">property>    bean>beans>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值