spring事务管理

spring-jdbc

需要的jar包:

mysql-connector-java
c3p0
spring-jdbc
spring-context

1.spring-jdbc中xml配置

1.1引入jdbc配置文件:

有两种方式

<!-- 方式1 contxt -->
<contxt:property-placeholder location="classpath:db.properties"></contxt:property-placeholder>

<!--方式2 util-->
<util:properties id="db" location="classpath:/db.properties"></util:properties>

1.2配置数据源(连接池)

要设置参数,交给spring容器管理

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
    <property name="user" value="#{db.username}"></property>
    <property name="password" value="#{db.password}"></property>
    <property name="jdbcUrl" value="#{db.url}"></property>
    <property name="driverClass" value="#{db.driver}"></property>
</bean>

1.3配置JdbcTemplate

交给容器管理

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

1.4开启包扫描和注解支持

<!--开启包扫描-->
<contxt:component-scan base-package="com.iweb.dao"></contxt:component-scan>
<!--开启注入注解支持-->
<contxt:annotation-config></contxt:annotation-config>

2.JdbcTemplate类,及常用api

针对数据库的操作,Spring框架提供了JdbcTemplate类,该类是Spring框架数据抽象层的基础,可以说,JdbcTemplate类是SpringJDBC的核心类

2.1execute

execute方法主要用来建表相关操作,平时很少使用

 String sql = "create table test (id integer, name varchar(100))";
jdbcTemplate.execute(sql);

2.2DML语句(表的增删改)

DML语句返回结果:影响的行数

增删改操作,都调用JdbcTemplate的update方法,括号内传一个sql语句即可

添加用户:

update会返回受影响的行数,添加时不能直接传入一个实体数据,内部无法自动取值,如果用?来占位,需要每个?都单独的去指定值

public void addUser(User user){
    int update = jdbcTemplate.update("insert  into user(name,age) values(?,?)", user.getName(),user.getAge());
    System.out.println(update);
}

更新用户:

public void update(User user){
    int update = jdbcTemplate.update("update user set name=?,age=? where id=?",
            user.getName(), user.getAge(), user.getId());
    System.out.println(update);

}

删除用户:

实际业务中,删除操作不会真正删除数据,而是将数据变为删除状态

public void delete(Integer id){
    int update = jdbcTemplate.update("delete from user where id =?", id);
    System.out.println(update);
}

2.3DQl语句(表查询)

query:

1.通用的查询方法,有多个同名方法的重载,使用BeanPropertyRowMapper做映射将返回对象封装成List。

2.BeanPropertyRowMapper对象会将数据库查询回的ResultSet自动封装为一个javaBean对象

3.底层采用代理机制,javaBean中的字段名必须和数据库的字段名一致,否则无法自动映射

query重载的方法有很多,常用的是传3个参数

依次是sql语句,Object的数组,BeanPropertyRowMapper对象

public User getUserById(Integer id){
        List<User> list = jdbcTemplate.query("select * from user where id=?", new Object[]{id},new BeanPropertyRowMapper<User>(User.class));
        return  list.get(0);
    }

queryForMap:

特点:

1.返回Map<String,Object>的查询结果,其中键是列名,值是表中对应的记录。

2.用于查询结果只有1条记录的情况。

3.返回多条记录报异常

public Map<String, Object> getALl(){
    Map<String, Object> map = jdbcTemplate.queryForMap("select * from user where id =?", 30021);
    System.out.println(map);
    return map;
}

queryForList:

特点:

1.返回多条记录的查询结果,封装成一个List集合

2.默认List集合中的每个元素是Map对象,即List<Map<String,Object>>

3.如果要封装成List对象,用query()方法。

public List<Map<String,Object>> queryForList(){
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(
                "select * from user1");
        return maps;
    }

测试:

@Test
public void test1(){
    ApplicationContext context =
            new ClassPathXmlApplicationContext("spring-jdbc.xml");
    UserDao userDao = context.getBean("userDao", UserDao.class);
    System.out.println(userDao.queryForList());
    //[{id=1, name=张三, age=18}, {id=2, name=李四, age=20}]
}

用RowMapper封装bean:

RowMapper接口定义了对象到列的映射关系,可以帮助我们在查询时自动封装bean

  JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
   List<User> user = jdbcTemplate.query("select * from user where id = ?"
                ,new RowMapper<User>() {
    @Override
     public User mapRow(ResultSet rs, int index) throws SQLException {
          User user = new User();
          user.setId(rs.getInt("id"));
          user.setName(rs.getString("name"));
          user.setAge(rs.getInt("age"));
          return user;
      }
   } ,
 2);
 System.out.println(user);

3.Spring 整合JDBC事务管理

3.1编程式事务管理

手动编写代码,在需要开启事务的地方手动控制事务的提交与回滚,编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate

3.2声明式事务管理

spring提供事务管理,配置方式(AOP实现)

1.导入配置文件

<!--导入配置文件-->
<util:properties id="db" location="db.properties"></util:properties>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
</bean>

2.配置数据源

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="jdbcUrl" value="#{db.url}"></property>
    <property name="user" value="#{db.username}"></property>
    <property name="password" value="#{db.password}"></property>
    <property name="driverClass" value="#{db.driver}"></property>
</bean>

3.配置事务管理器

<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--事务管理器,需要指定数据源
    此处就是去找id为dataSource的数据源-->
    <property name="dataSource" ref="dataSource"></property>
</bean>

4.配置事务通知

<!--配置事务通知,需要指定一个事务管理器
此处的事务管理器是id为transactionManager的管理器-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
        <tx:method name="update*"></tx:method>
        <tx:method name="delete*"></tx:method>
    </tx:attributes>
</tx:advice>

5.配置事务切面

<!--配置事务切面,需要指定一个事务通知
此处就是id为txAdvice的事务通知-->
<aop:config>
    <!--execution(* com.iweb.service.*.*(..)) 表示service包下所有类的所有方法都有效-->
    <aop:pointcut id="pc" expression="execution(* com.iweb.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
</aop:config>

3.3回滚机制

SpringJDBC内置的事务策略,只在底层抛出的异常是运行时异常时,才会回滚,其他异常不回滚,留给用户手动处理。

也可以在配置中指定在原有规则的基础上,哪些异常额外的回滚或不回滚

注意:

1.如果在一个业务逻辑中,需要有多步不同表的操作,此时应该在service层中完成对不同表的操作,以此保证多步操作处在同一个事务中。

2.切勿将业务代码在web层中调用不同Service来实现,虽然正常情况下也可以完成功能,但一旦出问题,很可能只有部分操作被回滚,数据出问题。

3.出现这种问题,不是事务管理机制的问题,而是开发者将业务逻辑错误的在web层中进行了实现,所以切记,web层只负责和用户交互和业务逻辑的调用,不进行任何业务逻辑的处理,任何业务逻辑都在service层中进行。

3.4注解方式

1.配置数据源

<!--导入配置文件-->
    <util:properties id="db" location="db.properties"></util:properties>
    <bean class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="#{db.url}"></property>
        <property name="user" value="#{db.username}"></property>
        <property name="password" value="#{db.password}"></property>
        <property name="driverClass" value="#{db.driver}"></property>
    </bean>

2.配置事务管理器

<!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--事务管理器,需要指定数据源
        此处就是去找id为dataSource的数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

3.开启包扫描和注解支持

<!--开启包扫描-->
<contxt:component-scan base-package="com.iweb"></contxt:component-scan>
<!--开启注解注入-->
<contxt:annotation-config></contxt:annotation-config>

4.开启事务注解支持

<!--开启事务注解支持-->
<tx:annotation-driven></tx:annotation-driven>

5.在需要事务支持的位置加上@Transactional注解

@Transactional:

1.声明式事务管理,应用在方法上时,这个方法的业务操作,spring会自动帮我们控制事务

2.只有运行时异常才能回滚事务

rollbackFor 可以指定什么异常回滚事务,例如:

//此处指定,只要是异常就回滚
@Transactional(rollbackFor = {java.lang.Throwable.class})

noRollbackFor 指定什么异常不回滚事务

propagation 可以指定事务的传播机制

propagation = Propagation.NOT_SUPPORTED 表示这个方法不支持事务,一般用在查询方法上

@Transactional(rollbackFor = {java.lang.Throwable.class},
               propagation = Propagation.NOT_SUPPORTED )

4.作用在类上时,表示这个类中所有方法的事务管理都有效

此时如果某个方法不需要事务,可以指定propagation = Propagation.NOT_SUPPORTED

3.5事务的传播机制

PROPAGATION_REQUIRED:

Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行

PROPAGATION_NOT_SUPPORT:

该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码

缓存

/**
 * 用户数据的缓存
 */
@Component
@Aspect
@Scope("singleton")
public class UserCache {
    //此map用来存储缓存数据
    /**
     * getArgs()获取目标方法的参数 ,返回的是一个Object数组
     */
    private Map<Integer,User> cache = new HashMap<>();
    @Around("execution( * com.iweb.dao.UserDaoMysqlImpl.getUserById(..))")
    public User cache(ProceedingJoinPoint pjp) throws Throwable {
        //获取目标参数,业务:获取查询数据库的id
        Integer id = (Integer)pjp.getArgs()[0];
        if(cache.containsKey(id)){//如果缓存中已经缓存这个id的user对象,返回这个缓存的user对象
            System.out.println("使用缓存的数据");
            return cache.get(id);
        }
        System.out.println("查询数据库");
        User user = (User)pjp.proceed();//调用目标方法,实际调用dao查询数据库
        cache.put(id,user);//向缓存中添加数据
        return user;
    }
}

spring事务管理

上一篇:SpringBoot - 读取JSON文件


下一篇:php和mysql关于时间的实用函数