springAOP事务控制xml:模仿 事务管理器:TransactionManager和连接工具类ConnectionUtils


我们自己手写编程事务管理器:TransactionManager和连接工具类:ConnectionUtils;
不用spring提供的管理器。

1. 配置javabean实体类

domain:Account
省略set和get方法,toString()方法

package com.jh.domain;
import java.io.Serializable;
/**
 * 账户的实体类:如果操作数据库,则属性名最好设置与(表)查询字段表一致
 * */
public class Account implements Serializable {
    private Integer id;
    private String name;
    private Float money;

2. service层

配置bean.xml

<!--配置Service层-->
    <bean id="accountService" class="com.jh.service.impl.AccountServiceImpl">
        <!--注入dao-->
        <property name="accountDao" ref="accountDao"></property>
    </bean>

接口

/**
 * 账户的业务层接口
 */
public interface IAccountService {
    //1. 查询所有
    List<Account> findAllAccount();
    //2.查询一个
    Account findAccountById(Integer accountId);
    //3.保存
    void saveAccount(Account account);
    //4.删除操作
    void deleteAccount(Integer accountId);
    //5.更新操作
    void updateAccount(Account account);
    //转账操作:转出账户名称sourceName;转入账户名称targetName;转账金额money
    void transfer(String sourceName, String targetName, Float money);
}

实现类

public class AccountServiceImpl implements IAccountService {
    private IAccountDao accountDao;

    public IAccountDao getAccountDao() {
        return accountDao;
    }

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public List<Account> findAllAccount() {
       return accountDao.findAllAccount();
    }

    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }

    public void saveAccount(Account account) {
        accountDao.saveAccount(account);
    }

    public void deleteAccount(Integer accountId) {
        accountDao.deleteAccount(accountId);
    }

    public void updateAccount(Account account) {
        accountDao.updateAccount(account);
    }

    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        System.out.println("transfer.....");
        //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);
        //2.6.更新转入账户
        accountDao.updateAccount(target);
    }
}

3. dao层

配置bean.xml

<!--配置Dao层-->
    <bean id="accountDao" class="com.jh.dao.impl.AccountDaoImpl">
        <!--注入QueryRunner对象-->
        <property name="runner" ref="runner"></property>
        <!--注入连接工具类:ConnectionUtils-->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
        <!--配置QueryRunner对象-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
    </bean>

接口和实现类

public interface IAccountDao {
    //1. 查询所有
    List<Account> findAllAccount();
    //2.查询一个
    Account findAccountById(Integer accountId);
    //3.保存
    void saveAccount(Account account);
    //4.删除操作
    void deleteAccount(Integer accountId);
    //5.更新操作
    void updateAccount(Account account);
    /**
     * 根据名称查询账户
     * 如果有唯一的一个结果就返回,如果没有结果就返回null;
     * 如果结果集超过一个就抛异常
     * */
    Account findAccountByName(String accountName);
}

持久层实现类写SQL语句

/**
 * 账户持久层的实现类
 * */
public class AccountDaoImpl implements IAccountDao {
    private QueryRunner runner;
    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    public QueryRunner getRunner() {
        return runner;
    }

    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }

    @Override
    public List<Account> findAllAccount() {
        try {
            //这里是在Spring里应用数据库连接(sql语句查询);
            //Account.class是查询的实体类关联,这里是List集合,用到BeanListHandler
            return runner.query(connectionUtils.getThreadConnection(),"select * from account",new BeanListHandler<Account>(Account.class));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Account findAccountById(Integer accountId) {
        try {
            //Account.class是查询的实体类关联,这里不是List集合,用BeanHandler
            return runner.query(connectionUtils.getThreadConnection(),"select * from account where id=?",
                    new BeanHandler<Account>(Account.class),accountId);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void saveAccount(Account account) {
        try {
            //account表中id是自增长,故不需要写
            runner.update(connectionUtils.getThreadConnection(),"insert into account(name,money)values(?,?)",
                    account.getName(),account.getMoney());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void deleteAccount(Integer accountId) {
        try {
            //问号占位符填的是传入参数值accountId
            runner.update(connectionUtils.getThreadConnection(),"delete from account where id=?",accountId);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void updateAccount(Account account) {
        try {
            /*每个问号代表一个占位符(1,2,3),后面的account.getName(),
            account.getMoney(),account.getId(),按顺序排列在前面的问号里*/
            runner.update(connectionUtils.getThreadConnection(),"update account set name=?,money=? where id=?",
                    account.getName(),account.getMoney(),account.getId());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Account findAccountByName(String accountName) {
        try {
            //这里是在Spring里应用数据库连接(sql语句查询);
            //Account.class是查询的实体类关联,这里是List集合,用到BeanListHandler
            List<Account>accounts= runner.query(connectionUtils.getThreadConnection(),"select * from account where name=?",new BeanListHandler<Account>(Account.class),accountName);
            if(accounts==null ||accounts.size()==0){
                return null;
            }if (accounts.size()>1){
                throw new RuntimeException("结果集不唯一,数据有问题");
            }
            return accounts.get(0);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

4. 事务管理器和工具类

配置bean.xml

    <!--配置数据源-->
    <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/maven?serverTimezone=GMT%2B8"></property>
        <property name="user" value="root"></property>
        <property name="password" value="jh7851192"></property>
    </bean>

    <!--配置Connection的工具类ConnectionUtils-->
    <bean id="connectionUtils" class="com.jh.utils.ConnectionUtils">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置事务管理器:TransactionManager-->
    <bean id="txManager" class="com.jh.utils.TransactionManager">
        <!--注入ConnectionUtils
            property name="connectionUtils"属性名就是TransactionManager类中的
            private ConnectionUtils connectionUtils的变量名connectionUtils一致
        -->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>

连接的工具类:ConnectionUtils

/**
 * 连接的工具类:它用于从数据源中获取一个连接,并且实现和线程的绑定
 * */
public class ConnectionUtils {
    private ThreadLocal<Connection>t1=new ThreadLocal<Connection>();
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    //获取当前线程上的连接
    public Connection getThreadConnection(){
        try {
            //1.先从ThreadLocal上获取
            Connection conn=t1.get();
            //2.判断当前线程上是否有连接
            if(conn==null){
                //3.从数据源中获取一个连接,并且存入ThreadLocal中
                conn=dataSource.getConnection();
                t1.set(conn);
            }
            //4.返回当前线程上的连接
            return conn;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    /**把连接和线程解绑*/
    public void removeConnection(){
        t1.remove();
    }
}

事务管理器 :TransactionManager

package com.jh.utils;
import java.sql.SQLException;
/**
 * 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
 * */
public class TransactionManager {
    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }
    //开启事务
    public void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    //提交事务
    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    //回滚事务
    public void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    //释放连接
    public void release(){
        try {
            connectionUtils.getThreadConnection().close();//还回连接池
            connectionUtils.removeConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

5.测试类(配置AOP)

配置AOP

<!--配置AOP-->
    <aop:config>
        <!--配置通用切入点-->
        <aop:pointcut id="pt1" expression="execution(* com.jh.service.impl.*.*(..))"></aop:pointcut>
        <aop:aspect id="txAdvice" ref="txManager"><!--txManager:TransactionManager作为通知类-->
            <!--前置通知:开启事务-->
            <aop:before method="beginTransaction" pointcut-ref="pt1"></aop:before>
            <!--配置后置通知:提交事务-->
            <aop:after-returning method="commit" pointcut-ref="pt1"></aop:after-returning>
            <!--异常通知:回滚事务-->
            <aop:after-throwing method="rollback" pointcut-ref="pt1"></aop:after-throwing>
            <!--最终通知:释放连接-->
            <aop:after method="release" pointcut-ref="pt1"></aop:after>
        </aop:aspect>
    </aop:config>

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")//locations是xml配置
public class AccountServiceTest {
 @Autowired
 private IAccountService as;

 @Test
 public void testTransfer(){
     as.transfer("aaa","bbb",-1200f);
 }

 @Test
 public void testFindAllaccount(){
     List<Account>accounts=as.findAllAccount();
     for(Account account:accounts){
         System.out.println(account);
     }
 }
}
发布了119 篇原创文章 · 获赞 15 · 访问量 7540

猜你喜欢

转载自blog.csdn.net/JH39456194/article/details/104609134