Spring JDBC和事务

1 篇文章 0 订阅
1 篇文章 0 订阅

一、Spring JDBC环境搭建

1.引入依赖(添加坐标)
<!--junit单元测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<!-- spring 测试环境 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>4.3.2.RELEASE</version>
    <scope>test</scope>
</dependency>

<!-- spring 框架坐标依赖添加 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>

<!-- aop -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>

<!-- mysql 驱动包 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.39</version>
</dependency>
<!-- c3p0 连接池 -->
<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>

<!-- spring jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>

<!-- spring事物 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>
2.添加db.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_jdbc?useUnicode=true&characterEncoding=utf8
jdbc.user=root
jdbc.password=123456
3.添加spring.xml文件
1.设置扫描器范围
2.加载properties配置文件
3.配置成c3p0数据源
4.配置JdbcTemplate 模板
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--设置扫描器范围-->
    <context:component-scan base-package="com.shsxt"/>

    <!-- 加载properties 配置文件 -->
    <context:property-placeholder location="db.properties"/>

    <!-- 配置c3p0 数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- jdbcTemplate 配置 -->
    <bean id="jdbcTemplate"  class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

</beans>
4.测试JDBC
@Test
public void testQueryCount() {

    // 得到Spring上下文环境
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
    // 得到模板类 JdbcTemplate对象
    JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("jdbcTemplate");

    Integer count = jdbcTemplate.queryForObject("select count(1) from account",Integer.class);
    Integer count1 = jdbcTemplate.queryForObject("select count(1) from account where userId = ?",Integer.class,1);

    System.out.println(count+"-------"+count1);
}

简易版本:

使用注解方式

@RunWith(SpringUnit4ClassRunner.class)

​ 将junit测试加到spring环境中

@ContextConfiguration(locations={“classpath:spring.xml”})

​ 设置加载的资源文件

1.定义父类

@RunWith(SpringJUnit4ClassRunner.class) // 将junit测试加到spring环境中
@ContextConfiguration(locations = {"classpath:spring.xml"}) // 设置要加载的资源文件
public class TestBase {

}

2.继承父类

public class SpringJdbcTest extends TestBase {

    @Resource
    private JdbcTemplate jdbcTemplate;

    @Test
    public void testQueryCount() {

        Integer count = jdbcTemplate.queryForObject("select count(1) from account",Integer.class);
        Integer count1 = jdbcTemplate.queryForObject("select count(1) from account where userId = ?",Integer.class,1);

        System.out.println(count+"-------"+count1);
}

二、Spring JDBC操作

1.添加操作

1.1定义接口和操作方法

public interface IAccountDao {
    /*
        1. 添加操作
           1.1 添加单条记录,返回受影响的行数
           1.2 添加单条记录,返回主键
           1.3 添加多条记录,返回受影响的行数
     */

    /**
     * 添加单条记录,返回受影响的行数
     * @param account
     * @return
     */
    public int addAccount(Account account);
    /**
     * 添加单条记录,返回主键
     * @param account
     * @return
     */
    public int addAccountHasKey(Account account);

    /**
     * 添加多条记录,返回受影响的行数
     * @param accounts
     * @return
     */
    public int addAccountBatch(List<Account> accounts);
}

1.2实现接口重写方法

/**
     * 添加单条记录,返回受影响的行数
     * @param account
     * @return
     */
    @Override
    public int addAccount(Account account) {
        String sql = "insert into account(accountName,accountMoney,accountRemark,userId,createTime,updateTime) values (?,?,?,?,now(),now())";
        Object[] objs = {account.getAccountName(),account.getAccountMoney(),account.getAccountRemark(),account.getUserId()};
        return jdbcTemplate.update(sql,objs);
    }
    /**
     * 添加单条记录,返回主键
     * @param account
     * @return
     */
    @Override
    public int addAccountHasKey(Account account) {
        String sql = "insert into account(accountName,accountMoney,accountRemark,userId,createTime,updateTime) values (?,?,?,?,now(),now())";
        // 定义keyHolder 对象  用户获取记录主键值
        KeyHolder keyHolder = new GeneratedKeyHolder();
        jdbcTemplate.update(new PreparedStatementCreator() {
            @Override
            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                // 预编译sql语句,并设置返回主键
                PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
                // 设置参数
                ps.setString(1,account.getAccountName());
                ps.setDouble(2,account.getAccountMoney());
                ps.setString(3,account.getAccountRemark());
                ps.setInt(4,account.getUserId());
                return ps;
            }
        },keyHolder);
        // 得到返回的主键
        Integer key = keyHolder.getKey().intValue();

        return key;
    }
    /**
     * 添加多条记录,返回受影响的行数
     * @param accounts
     * @return
     */
    @Override
    public int addAccountBatch(final List<Account> accounts) {
        String sql = "insert into account(accountName,accountMoney,accountRemark,userId,createTime,updateTime) values (?,?,?,?,now(),now())";
        int rows = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
                // 设置参数
                preparedStatement.setString(1,accounts.get(i).getAccountName());
                preparedStatement.setDouble(2,accounts.get(i).getAccountMoney());
                preparedStatement.setString(3,accounts.get(i).getAccountRemark());
                preparedStatement.setInt(4,accounts.get(i).getUserId());
            }
            @Override
            public int getBatchSize() {
                return accounts.size();
            }
        }).length;

        return rows;
    }

1.3测试类

 	@Test
    public void testAddAccount() {
        Account account = new Account("wangwu",500.0,2,"第一桶金");
        int row = accountDao.addAccount(account);
        System.out.println(row);
    }

    @Test
    public void testAddAccountHasKey() {
        Account account = new Account("zhaoliu",5000.0,2,"早餐费");
        int key = accountDao.addAccountHasKey(account);
        System.out.println(key);
    }

    @Test
    public void testAddAccountBatch() {
        List<Account> list = new ArrayList<>();
        for(int i = 1; i <= 5; i++) {
            Account account = new Account("用户"+i,1000.0,2,"小费"+i);
            list.add(account);
        }
        int rows = accountDao.addAccountBatch(list);
        System.out.println(rows);
    }

2.查询操作

2.1定义方法

 /*
      2、查询操作
             2.1 查询总数量
             2.2 查询对象
             2.3 查询集合
     */

    /**
     * 通过用户ID查询账户总记录数
     * @param userId
     * @return
     */
    public int queryTotalCount(int userId);

    /**
     * 通过主键查询账户对象
     * @param accountId
     * @return
     */
    public Account queryAccount(int accountId);

    /**
     * 通过指定条件分页查询指定用户下的账户列表
     * @param userId
     * @param accountName
     * @param createTime
     * @param pageNum
     * @param pageSize
     * @return
     */
    public List<Account> queryAccountList(int userId,String accountName,String createTime,int pageNum,int pageSize);

2.2实现方法

  /**
     * 通过用户ID查询账户总记录数
     * @param userId
     * @return
     */
    @Override
    public int queryTotalCount(int userId) {
        String sql = "select count(1) from account where userId = ?";
        int count = jdbcTemplate.queryForObject(sql,Integer.class,userId);
        return count;
    }

    /**
     * 通过主键查询账户对象
     * @param accountId
     * @return
     */
    @Override
    public Account queryAccount(int accountId) {
        String sql = "select accountId,accountName,accountMoney,accountRemark,createTime,updateTime,userId from account where accountId = ?";
        Account account = jdbcTemplate.queryForObject(sql, new Object[]{accountId}, new RowMapper<Account>() {
            @Override
            public Account mapRow(ResultSet resultSet, int i) throws SQLException {
                Account acc = new Account();
                acc.setAccountId(resultSet.getInt("accountId"));
                acc.setAccountMoney(resultSet.getDouble("accountMoney"));
                acc.setAccountName(resultSet.getString("accountName"));
                acc.setAccountRemark(resultSet.getString("accountRemark"));
                acc.setCreateTime(resultSet.getDate("createTime"));
                acc.setUpdatetime(resultSet.getDate("updateTime"));
                acc.setUserId(resultSet.getInt("userId"));
                return acc;
            }
        });

        return account;
    }

    /**
     * 通过指定条件分页查询指定用户下的账户列表
     * @param userId
     * @param accountName
     * @param createTime
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Override
    public List<Account> queryAccountList(int userId, String accountName, String createTime, int pageNum, int pageSize) {

        String sql = "select accountId,accountName,accountMoney,accountRemark,createTime,updateTime,userId from account where userId = ? ";
        List<Object> params = new ArrayList<>();
        params.add(userId);

        // 判断是否有条件查询
        if (StringUtils.isNotBlank(accountName)) {
            sql += " and  accountName like concat('%',?,'%') ";
            params.add(accountName);
        }
        if (StringUtils.isNotBlank(createTime)) {
            sql += " and createTime > ? ";
            params.add(createTime);
        }

        // 分页
        sql += " limit ?,? ";
        params.add((pageNum-1)*pageSize);
        params.add(pageSize);

        // 将集合转换成数组
        Object[] objs = params.toArray();

        List<Account> accountList = jdbcTemplate.query(sql, objs, new RowMapper<Account>() {
            @Override
            public Account mapRow(ResultSet resultSet, int i) throws SQLException {
                Account acc = new Account();
                acc.setAccountId(resultSet.getInt("accountId"));
                acc.setAccountMoney(resultSet.getDouble("accountMoney"));
                acc.setAccountName(resultSet.getString("accountName"));
                acc.setAccountRemark(resultSet.getString("accountRemark"));
                acc.setCreateTime(resultSet.getDate("createTime"));
                acc.setUpdatetime(resultSet.getDate("updateTime"));
                acc.setUserId(resultSet.getInt("userId"));
                return acc;
            }
        });

        return accountList;
    }

2.3测试类

 @Test
    public void testQueryTotalCount() {
        System.out.println(accountDao.queryTotalCount(1));
    }

    @Test
    public void testQueryAccount() {
        System.out.println(accountDao.queryAccount(1));
    }

    @Test
    public void testQueryAccountList() {
   System.out.println(accountDao.queryAccountList(2,"","",1,5).size());
        System.out.println(accountDao.queryAccountList(2,"u","2019-12-20",1,5));
    }

3.查询操作

3.3定义方法

/*
        3、修改操作
           3.1 修改单条记录,返回受影响的行数
           3.2 修改多条记录,返回受影响的行数
     */
    /**
     * 修改单条记录,返回受影响的行数
     * @param account
     * @return
     */
    public int updateAccount(Account account);

    /**
     * 修改多条记录,返回受影响的行数
     * @param accounts
     * @return
     */
    public int updateAccountBatch(List<Account> accounts);

3.2实现方法

 /**
     * 修改账户信息
     * @param account
     * @return
     */
    @Override
    public int updateAccount(Account account) {
        String sql = "update account set accountName = ?, accountMoney = ? ,accountRemark = ?,userId = ? ,updateTime = now() where accountId = ? ";
        Object[] objs = {account.getAccountName(),account.getAccountMoney(),account.getAccountRemark(),account.getUserId(),account.getAccountId()};
        return jdbcTemplate.update(sql,objs);
    }

    /**
     * 批量修改账户信息
     * @param accounts
     * @return
     */
    @Override
    public int updateAccountBatch(List<Account> accounts) {
        String sql = "update account set accountName = ?, accountMoney = ? ,accountRemark = ?,userId = ? ,updateTime = now() where accountId = ? ";
        int rows = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                // 设置参数
                ps.setString(1,accounts.get(i).getAccountName());
                ps.setDouble(2,accounts.get(i).getAccountMoney());
                ps.setString(3,accounts.get(i).getAccountRemark());
                ps.setInt(4,accounts.get(i).getUserId());
                ps.setInt(5,accounts.get(i).getAccountId());
            }
            @Override
            public int getBatchSize() {
                return accounts.size();
            }
        }).length;
        return rows;
    }

3.3测试类

  @Test
    public void testupdateAccount() {
        Account account = new Account("adminManager",200.0,1,"最后一桶金");
        account.setAccountId(1);
        int row = accountDao.updateAccount(account);
        System.out.println(row);
    }

    @Test
    public void testUpdateAccountBatch() {
        List<Account> list = new ArrayList<>();
        for(int i = 6; i <= 10; i++) {
            Account account = new Account("账号"+i,10000.0,2,"工资"+i);
            account.setAccountId(i);
            list.add(account);
        }
        int rows = accountDao.updateAccountBatch(list);
        System.out.println(rows);
    }
4.删除操作

4.1定义方法

   /*
         4、删除操作
           4.1 删除单条记录,返回受影响的行数
           4.2 删除多条记录,返回受影响的行数
     */

    /**
     * 删除单条记录,返回受影响的行数
     * @param accountId
     * @return
     */
    public int deleteAccount(int accountId);


    /**
     * 删除多条记录,返回受影响的行数
     * @param ids
     * @return
     */
    public int deleteAcccountBatch(int[] ids);

4.2实现方法

/**
     * 通过账户ID删除账户记录
     * @param accountId
     * @return
     */
    @Override
    public int deleteAccount(int accountId) {
        String sql = "delete from account where accountId= ? ";
        Object[] objs = {5};
        return jdbcTemplate.update(sql,objs);
    }

    /**
     * 批量删除账户
     * @param ids
     * @return
     */
    @Override
    public int deleteAcccountBatch(int[] ids) {
        String sql = "delete from account where accountId = ?";
        int row = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setInt(1,ids[i]);
            }

            @Override
            public int getBatchSize() {
                return ids.length;
            }
        }).length;
        return row;
    }

4.3测试类

  @Test
    public void testDeleteAccount() {
        System.out.println(accountDao.deleteAccount(5));
    }

    @Test
    public void testDeleteAccountBatch() {
        System.out.println(accountDao.deleteAcccountBatch(new int[]{6,7,8,9,10}));
    }

5.转账场景代码模拟
public class AccountService {

    @Resource
    private AccountDao accountDao;

    /**
     * 张三用户给李四用户转账100
     *  张三的金额 - 100
     *  李四的金额 + 100
     * @return
     */
    //事务处理
    //@Transactional(propagation= Propagation.REQUIRED)
    public void toupdateTransMoney() {
        // 张三用户
        Account zhangsan =  accountDao.queryAccount(3);

        // 李四用户
        Account lisi = accountDao.queryAccount(2);

        // 张三的金额 - 100
        zhangsan.setAccountMoney(zhangsan.getAccountMoney()-100);
        // 修改张三的金额
        int r1 = accountDao.updateAccount(zhangsan);

       // int i = 1/0;

        // 李四的金额 + 100
        lisi.setAccountMoney(lisi.getAccountMoney()+100);
        // 修改李四的金额
        int r2 = accountDao.updateAccount(lisi);

        if (r1 == 1 && r2 == 1) {
            System.out.println("转账成功!");
        } else {
            System.out.println("转账失败!");
        }
    }
}

测试类

  @Test
    public void testTransMoney() {
        accountService.toupdateTransMoney();
    }

三、Spring事务控制配置两种方式

1.xml事务配置
1.1 修改xml命名空间,添加
xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd

1.2 aop代理和aop配置(切入点、通知)
 <!--设置扫描器范围-->
    <context:component-scan base-package="com.shsxt"/>

<!-- 开启AOP代理 -->
   <aop:aspectj-autoproxy />

 <!--配置aop(切入点、通知)-->
    <aop:config>
        <!--设置切入点  设置需要被拦截的方法-->
        <aop:pointcut id="cut" expression="execution(* com.shsxt.service..*.*(..))"/>
        <!--设置通知   事务通知-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="cut"/>
    </aop:config>
1.3 配置事务管理器
<!-- 事务管理器定义 -->
    <bean id="txManager"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--数据源 -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
1.4 配置事务相关通知
 <!-- 配置事务通知   transaction-manager表示事务通知是某个事务管理器管理的-->
    <!--
        tx:method的属性:
          * name 是必须的,表示与事务属性关联的方法名(业务方法名),对切入点进行细化。通配符(*)可以用来指定一批关联到相同的事务属性的方法。
            如:'get*'、'handle*'、'on*Event'等等.
           propagation 不是必须的 ,默认值是REQUIRED
                表示事务传播行为, 包括REQUIRED,SUPPORTS,MANDATORY,REQUIRES_NEW,NOT_SUPPORTED,NEVER,NESTED
           isolation  不是必须的 默认值DEFAULT
                表示事务隔离级别(数据库的隔离级别)
           timeout 不是必须的 默认值-1(永不超时)
                表示事务超时的时间(以秒为单位)
           read-only 不是必须的 默认值false不是只读的
                表示事务是否只读
           rollback-for 不是必须的
                表示将被触发进行回滚的 Exception(s);以逗号分开。
                如:'com.foo.MyBusinessException,ServletException'
           no-rollback-for 不是必须的
                表示不被触发进行回滚的 Exception(s);以逗号分开。
                如:'com.foo.MyBusinessException,ServletException'
            任何 RuntimeException 将触发事务回滚
    -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!--对以add update delete query开头饿所有方法进行事务处理-->
        <tx:attributes>
            <!--定义什么方法需要使用事务  name代表的是方法名(或方法匹配)-->
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="delete*" propagation="REQUIRED" />
            <tx:method name="query*" read-only="true" />
        </tx:attributes>
    </tx:advice>
1.5 配置注解支持
<!--配置事务注解支持-->
    <tx:annotation-driven transaction-manager="txManager"/>
2.注解事务配置
2.1 配置事务管理器
 <!-- 事务管理器定义 -->
    <bean id="txManager"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--数据源 -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
2.2 配置注解支持
 <!--配置事务注解支持-->
    <tx:annotation-driven transaction-manager="txManager"/>
2.3 Service方法上在需要添加事务的方法上加入事物注解

注解: @Transactional(propagation= Propagation.REQUIRED)

    public void toupdateTransMoney() {
        // 张三用户
        Account zhangsan =  accountDao.queryAccount(3);

        // 李四用户
        Account lisi = accountDao.queryAccount(2);

        // 张三的金额 - 100
        zhangsan.setAccountMoney(zhangsan.getAccountMoney()-100);
        // 修改张三的金额
        int r1 = accountDao.updateAccount(zhangsan);

       // int i = 1/0;

        // 李四的金额 + 100
        lisi.setAccountMoney(lisi.getAccountMoney()+100);
        // 修改李四的金额
        int r2 = accountDao.updateAccount(lisi);

        if (r1 == 1 && r2 == 1) {
            System.out.println("转账成功!");
        } else {
            System.out.println("转账失败!");
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值