Spring-Jdbc/Transaction

2021-06-17 16:49:42 浏览数 (1)

# Spring-JDBC与Spring 事务

# 使用c3p0链接池配置信息
代码语言:javascript复制
jdbc.user=root
jdbc.password=xxxxxx
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/Xxxx?useSSL=false&serverTimezone=UTC

jdbc.initPoolSize=5
jdbc.maxPoolSize=20
# spring配置文件
代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!--导入资源配置文件-->
    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>

        <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
        <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>

    </bean>

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

</beans>
# spring-jdbc测试
代码语言:javascript复制
package top.finen.spring.jdbc;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class JDBCTest {
    private ApplicationContext ctx = null;
    private JdbcTemplate jdbcTemplate;

    {
        ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");
    }

    /**
     * 批量更新
     * 最后一个参数是Object[]的List类型:因为修改一条记录需要一个Object的数组,那么多条就需要多个Object数组
     */
    @Test
    public void testBatchUpdate() {
        String sql = "INSERT INTO employees(last_name, email, dept_id) VALUES(?, ?, ?)";
        List<Object[]> batchArgs = new ArrayList<>();
        batchArgs.add(new Object[]{"AA", "88888@qq.com", 1});
        batchArgs.add(new Object[]{"BB", "88888@qq.com", 1});
        batchArgs.add(new Object[]{"CC", "88888@qq.com", 3});
        batchArgs.add(new Object[]{"VF", "88888@qq.com", 1});
        batchArgs.add(new Object[]{"FFF", "88888@qq.com", 1});
        batchArgs.add(new Object[]{"AGGA", "88888@qq.com", 1});
        batchArgs.add(new Object[]{"WW", "88888@qq.com", 1});
        jdbcTemplate.batchUpdate(sql, batchArgs);
    }

    @Test
    public void testUpdate() {
        String sql = "UPDATE employees SET last_name = ? WHERE id = ?";
        jdbcTemplate.update(sql, "Tom", 3);
    }


    @Test
    public void testDataSource() throws SQLException {
        DataSource dataSource = ctx.getBean(DataSource.class);
        System.out.println(dataSource.getConnection());
    }


    /**
     * 从数据库中获取一条记录,实际得到对应的一个对象
     * 注意:不是调用queryForObject(String sql, Class<Employee> requireType, Object... args)方法,
     * 而需要调用queryForObject(String sql, RowMapper<T> rowMapper, Object... args)
     * 1. 其中的RowMapper指定如何如映射结果集的行,常用的实现类为BeanPropertyRowMapper
     * 2.使用SQL中的列的别名完成列名和类的属性名的映射,例如:last_name lastName
     * 3.不支持级联属性,JdbcTemplate到底是一个Jdbc的小工具,而不是ORM框架
     */
    @Test
    public void testQueryForObject() {

        String sql = "SELECT id, last_name lastName, email FROM employees WHERE id = ?";
        RowMapper<Employee> employeeRowMapper = new BeanPropertyRowMapper<Employee>(Employee.class);
        Employee employee = jdbcTemplate.queryForObject(sql, employeeRowMapper, 1);
        System.out.println(employee);

    }

    /**
     * 查到实体类的集合
     * 注意调用不是的queryForList
     */
    @Test
    public void testQueryForList() {
        String sql = "SELECT id, last_name lastName, email FROM employees WHERE id > ?";
        RowMapper<Employee> employeeRowMapper = new BeanPropertyRowMapper<Employee>(Employee.class);
        List<Employee> employees = jdbcTemplate.query(sql, employeeRowMapper, 3);
        System.out.println(employees);
    }

    /**
     * 获取单列的值或者做统计查询
     *
     */
    @Test
    public void testQueryForObject2() {

        String sql = "SELECT count(id) FROM employees";
        long count = jdbcTemplate.queryForObject(sql, Long.class);

        System.out.println(count);

    }


}
# Spring使用具名参数
代码语言:javascript复制
<!--配置namedParameterJdbcTemplate,该对象可以使用具名参数,其没有无参数的构造器指定参数-->
<bean id="namedParameterJdbcTemplate"
      class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
    <constructor-arg ref="dataSource"></constructor-arg>
</bean>
代码语言:javascript复制
@Test
public void testNamedParameterJdbcTemplate() {
    String sql = "INSERT INTO employees(last_name, email, dept_id) VALUES(:ln, :email, :deptid)";
    Map<String, Object> paraMap = new HashMap<>();
    paraMap.put("ln", "FF");
    paraMap.put("email", "sss@888.com");
    paraMap.put("deptid", 2);
    namedParameterJdbcTemplate.update(sql, paraMap);
}

/**
 * 使用具名参数时,可以update(String sql, SqlParameterSource paramSource)方法进行更新操作
 * 1.SQL语句的参数与类的属性一致
 * 2.使用SqlParameterSource的BeanPropertySqlParameterSource实现类作为参数
 */
@Test
public void testNamedParameterJdbcTemplate2() {
    String sql = "INSERT INTO employees(last_name, email, dept_id) VALUES(:lastName, :email, :deptId)";
    Employee employee = new Employee();
    employee.setLastName("sssss");
    employee.setEmail("@@@ssss.com");
    employee.setDeptId(1);
    SqlParameterSource sqlParameterSource = new BeanPropertySqlParameterSource(employee);
    namedParameterJdbcTemplate.update(sql, sqlParameterSource);
}

@Test
public void testEmployeeDao() {
    System.out.println(employeeDao.get(1));
}
# Spring的事物管理
代码语言:javascript复制
<!--配置事物管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!--启用事物注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
代码语言:javascript复制
@Transactional
@Override
public void purchase(String username, String isbn) {
    //1.获取书的单价
    int price = bookShopDao.findBookPriceByIsbn(isbn);

    //2.更新书的库存
    bookShopDao.updateBookStock(isbn);

    //3.更新用户余额
    bookShopDao.updateUserAccount(username, price);
}
# Spring 事物传播属性、事物隔离级别、回滚

当事务方法被另一个事务方法调用时,必须制定事务应该如何传播。

事务的传播行为可以有传播属性指定,Spring订了7种类型的传播行为。

REQUIRED 如果有事务在运行,当前的方法就在这个内运行,否则,就启动另一个事务,并在自己的事务内运行。

REQUIRED_NEW 当前的方法必须启动新事务,并在他自己的事务内运行,如果有事务在运行,应该将它挂起。

SUPPORTS 如果有事务在运行,当前的方法就在这个事物内运行,否则它可以不运行在事务中。

NOT_SUPPORTS 当前的方法不应该运行在事物中,如果有运行的事务,将它挂起。

MANDATORY 当前方法必须运行在事务内部,如果没有正在运行的事务。就跑出异常。

NESTED 如果有事务在运行,当前的方法就应该在这个事务的嵌套事物内运行,否则,就启动一个新的事务。并在他自己的事务内运行。

代码语言:javascript复制
/**
     * 添加事物注解
     * 使用propagation指定事务的传播行为,即当前事务方法被另一个事物调用时,如何使用事务
     * 默认取值为REQUIRED,即使用调用方法的事务
     * 使用isolation指定事物的隔离级别,最常用取值为READ_COMMITTED
     * 默认情况下Spring的声明式事物对所有的运行异常进行回滚。也可以通过对应的属性进行设置。通常情况下去默认值即可。
     * 使用readOnly指定是否只读,表示这个事物只读取数据但不更新数据,这样可以帮助数据库引擎优化事物,若真的是一个只读取数据的方法,应设置readOnly为true
     * 使用timeout指定强制回滚之前事务可以占用的时间
     * @param username
     * @param isbns
     */
    @Transactional(propagation = Propagation.REQUIRED,
            isolation = Isolation.READ_COMMITTED,
            timeout = 1000,
            readOnly = false,
            noRollbackFor = {UserAccountException.class})
    @Override
    public void checkout(String username, List<String> isbns) {
        for (String isbn: isbns) {
            bookShopService.purchase(username, isbn);
        }
    }
# 基于xml的配置
代码语言:javascript复制
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
 <context:component-scan base-package="top.finen.spring"></context:component-scan>

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

     <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
     <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
 </bean>

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

 <!-- 配置 bean -->
 <bean id="bookShopDao1" class="top.finen.spring.tx.xml.BookShopDaoImpl">
     <property name="jdbcTemplate" ref="jdbcTemplate1"></property>
 </bean>

 <bean id="bookShopService1" class="top.finen.spring.tx.xml.BookShopServiceImpl">
     <property name="bookShopDao" ref="bookShopDao1"></property>
 </bean>

 <bean id="cashier1" class="top.finen.spring.tx.xml.CashierImpl">
     <property name="bookShopService" ref="bookShopService1"></property>
 </bean>

 <!--配置事务管理器-->
 <bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <property name="dataSource" ref="dataSource1"></property>
 </bean>

 <!--事务属性-->
 <tx:advice id="txAdvice" transaction-manager="transactionManager2">
     <tx:attributes>
         <tx:method name="*"/>
     </tx:attributes>
 </tx:advice>

 <!--配置事务切入点,以及把事务切入点和事务属性关联-->
<aop:config>
    <aop:pointcut id="txPointcut" expression="execution(* top.finen.spring.tx.xml.BookShopService.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"></aop:advisor>
</aop:config>

0 人点赞