专栏首页Java开发者之家Spring-Jdbc/Transaction

Spring-Jdbc/Transaction

# Spring-JDBC与Spring 事务

# 使用c3p0链接池配置信息

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配置文件

<?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测试

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使用具名参数

<!--配置namedParameterJdbcTemplate,该对象可以使用具名参数,其没有无参数的构造器指定参数-->
<bean id="namedParameterJdbcTemplate"
      class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
    <constructor-arg ref="dataSource"></constructor-arg>
</bean>
@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的事物管理

<!--配置事物管理器-->
<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>
@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 如果有事务在运行,当前的方法就应该在这个事务的嵌套事物内运行,否则,就启动一个新的事务。并在他自己的事务内运行。

/**
     * 添加事物注解
     * 使用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的配置

<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 条评论
登录 后参与评论

相关文章

  • Spring-Spring MVC + Spring JDBC + Spring Transaction + Maven 构建web登录模块

    请参考上篇博文 Maven-EclipseEE使用Maven构建Java web项目从0到1

    小小工匠
  • Spring JDBC-事务管理中的多线程问题

    众所周知,Spring 的事务管理器是通过线程相关的 ThreadLocal 来保存数据访问基础设施,再结合 IOC 和 AOP 实现高级声明式事务的功能,所以...

    小小工匠
  • Spring JDBC-混合框架的事务管理

    很难说哪种数据访问技术是最优秀的,只有在某种特定的场景下,才能给出答案。所以在一个应用中,往往采用多个数据访问技术:一般是两种,一种采用 ORM 技术框架,而另...

    小小工匠
  • 项目中Spring 声明式事务使用的一些坑点分析01

    秋日芒草
  • REQUIRES_NEW导致数据库连接死锁

    获取数据库连接的时间居然超过了30秒,正常情况下一个请求的处理时间是200ms,所以觉得特别奇怪。 按说即使数据库连接数小于请求并发数,因为数据库连接是共享的,...

    十毛
  • spring事务源码解析

      在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内...

    青石路
  • Spring整合mybatis中的sqlSession是如何做到线程隔离的?

    项目中常常使用mybatis配合spring进行数据库操作,但是我们知道,数据的操作是要求做到线程安全的,而且按照原来的jdbc的使用方式,每次操作完成之后都要...

    用户5546570
  • Spring 是如何保证一个事务内获取同一个Connection

    Spring有声明式事务和编程式事务,声明式事务只需要提供 @Transactional的注解。然后事务的开启、提交、回滚、资源的清理都由Spring 来管理,...

    王小明_HIT
  • Spring JDBC-实施Spring AOP事务注意事项及案例分析

    众所周知,Spring事务管理是基于接口代理或动态字节码技术,通过AOP实施事务增强的,虽然Spring也支持AspectJ LTW在类加载期实施增强,但这种方...

    小小工匠
  • Spring 数据库连接(Connection)绑定线程(Thread)的实现

    最近在看spring事务的时候在想一个问题:spring中的很多bean都是单例的,是非状态的,而数据库连接是一种有状态的对象,所以spring一定在创建出co...

    老白
  • Spring事务源码解析(三)

    在之前的文章Spring事务源码解析(二)获取增强中,我们分析了Spring事务的实现是基于AOP实现的,还分析了增强BeanFactoryTransactio...

    Java学习录
  • 阿里3面:Spring声明式事务连环炮,让我措手不及。。

    这篇主要介绍声明式事务的用法,我们在工作中基本上用的都是声明式事务,所以这篇文章是比较重要的,建议各位打起精神,正式开始。

    路人甲Java
  • 三问Spring事务:解决什么问题?如何解决?存在什么问题?

    让我们先从事务说起,“什么是事务?我们为什么需要事务?”。事务是一组无法被分割的操作,要么所有操作全部成功,要么全部失败。我们在开发中需要通过事务将一些操作组成...

    草捏子
  • 关于spring和springboot的transactionManager那些事

    注解的方式主要是使用tx:annotation-driven实现的,这样在代码里的方法上或类上就可以使用@Transactional注解来灵活地控制事务了。

    开发架构二三事
  • 揭秘JDBC超时机制

    在遭到DDos攻击后,整个服务都垮掉了。由于第四层交换机不堪重负,网络变得无法连接,从而导致业务系统也无法正常运转。安全组很快屏蔽了所有的DDos攻击,并恢复了...

    Bug开发工程师
  • 基于可靠消息方案的分布式事务(二):Java中的事务

    aoho求索
  • SpringBoot中使用redis事务

    首先从使用springboot+redis碰到的一个问题说起。在前几篇文章中介绍了用SpringBoot+redis构建了一个个人博客。在刚开始远行的时候发现发...

    林老师带你学编程
  • Spring JDBC-事务方法嵌套调用解读

    关于Spring事务的一个错误的说法:一个事务方法中不应该调用另外一个事务方法,否则将产生两个事务,其实这是不正确的。

    小小工匠
  • MyBatis事务管理机制

    MyBatis作为Java语言的数据库框架,对数据库的事务管理是其非常重要的一个方面。本文将从事务的分类、配置和实现分析MyBatis的事务管理的实现机制。

    java架构师
  • Spring Boot配置属性

    itliusir

扫码关注云+社区

领取腾讯云代金券