Spring 事务介绍(二)之 事务的传播机制

spring.png

Spring 事物介绍(二)之 事物的传播机制

Spring中对事物的支持

Spring 事物相关API:

spring事物是在数据库事物的基础上进行封装扩展,其主要特性如下:

  • 支持原有的数据事物的隔离级别
  • 加入了事物传播的概念,提供多个事物的合并和隔离的功能
  • 提供声明式事物,让业务代码与事物分离,事物更易用

spring提供了三个接口用来使用事物:

  • TransactionDefinition :事物定义
  • PlatformTransactionManager :事物的管理
  • TransactionStatus : 事物运行状态

TransactionDefinition.java

package org.springframework.transaction;

public interface TransactionDefinition {
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;
    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = 1;
    int ISOLATION_READ_COMMITTED = 2;
    int ISOLATION_REPEATABLE_READ = 4;
    int ISOLATION_SERIALIZABLE = 8;
    int TIMEOUT_DEFAULT = -1;

    int getPropagationBehavior();

    int getIsolationLevel();

    int getTimeout();

    boolean isReadOnly();

    String getName();
}

PlatformTransactionManager.java

package org.springframework.transaction;

public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException;

    void commit(TransactionStatus var1) throws TransactionException;

    void rollback(TransactionStatus var1) throws TransactionException;
}

TransactionStatus.java

package org.springframework.transaction;

import java.io.Flushable;

public interface TransactionStatus extends SavepointManager, Flushable {
    boolean isNewTransaction();

    boolean hasSavepoint();

    void setRollbackOnly();

    boolean isRollbackOnly();

    void flush();

    boolean isCompleted();
}

API应用demo:

pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.demo.spring</groupId>
    <artifactId>spring-tx-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spring.version>4.3.8.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.35</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>4.0.4.RELEASE</version>
        </dependency>
    </dependencies>
</project>

SpringTransactionTest.java

package com.demo.spring;

import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/13 20:51
 */
public class SpringTransactionTest {

    private static String jdbcUrl = "jdbc:mysql://192.168.5.104:3306/spring";
    private static String userName = "root";
    private static String password = "root";

    public static Connection openConnection() throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection(jdbcUrl, userName, password);
        return conn;
    }

    public static void main(String[] args) {
        final DataSource dataSource = new DriverManagerDataSource(jdbcUrl, userName, password);
        TransactionTemplate template = new TransactionTemplate();
        template.setTransactionManager(new DataSourceTransactionManager(dataSource));

        template.execute(new TransactionCallback<Object>() {
            public Object doInTransaction(TransactionStatus transactionStatus) {
                Connection connection = DataSourceUtils.getConnection(dataSource);
                Object savePoint = null;

                try {
                    {
                        PreparedStatement preparedStatement = connection.prepareStatement(
                                "insert into account(accountName,user,money) VALUES (?,?,?)");
                        preparedStatement.setString(1,"111");
                        preparedStatement.setString(2,"a");
                        preparedStatement.setInt(3,100);
                        preparedStatement.executeUpdate();
                    }
                    savePoint = transactionStatus.createSavepoint();

                    {
                        PreparedStatement preparedStatement = connection.prepareStatement(
                                "insert into account(accountName,user,money) VALUES (?,?,?)");
                        preparedStatement.setString(1,"222");
                        preparedStatement.setString(2,"b");
                        preparedStatement.setInt(3,100);
                        preparedStatement.executeUpdate();
                    }
                    {
                        PreparedStatement preparedStatement = connection.prepareStatement(
                            "update account set money= money+1 where user=?");
                        preparedStatement.setString(1,"333");
                        //手动设置异常
                        int i = 1/0;
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    System.out.println("update failed");
                    if (savePoint != null) {
                        transactionStatus.rollbackToSavepoint(savePoint);
                    } else {
                        transactionStatus.setRollbackOnly();
                    }
                }
                return null;
            }
        });
    }
}

执行结果:

update failed

但是savePoint之前的插入是成功的。

这是因为

 public Object doInTransaction(TransactionStatus transactionStatus) {
            Connection connection = DataSourceUtils.getConnection(dataSource);
            ...
}            

TransactionStatus transactionStatus中的Connection与Connection与DataSourceUtils.getConnection(dataSource)返回的是同一个Connection。

相关源码:

org.springframework.transaction.support.TransactionSynchronizationManager#bindResource

public static void bindResource(Object key, Object value) throws IllegalStateException {
    Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
    Assert.notNull(value, "Value must not be null");
    Map<Object, Object> map = (Map)resources.get();
    if (map == null) {
        map = new HashMap();
        resources.set(map);
    }
    
    Object oldValue = ((Map)map).put(actualKey, value);
    if (oldValue instanceof ResourceHolder && ((ResourceHolder)oldValue).isVoid()) {
        oldValue = null;
    }

    if (oldValue != null) {
        throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
    } else {
        if (logger.isTraceEnabled()) {
            logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]");
        }

    }
}

其中((Map)map).put(actualKey, value)中的

actualKey

org.springframework.jdbc.datasource.DriverManagerDataSource

value

org.springframework.jdbc.datasource.ConnectionHolder

这个map已经放入到resources中了,resources是一个ThreadLocal,取的时候也去这里面取

private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");

get时的相关源码

org.springframework.transaction.support.TransactionSynchronizationManager#doGetResource

private static Object doGetResource(Object actualKey) {
    Map<Object, Object> map = (Map)resources.get();
    if (map == null) {
        return null;
    } else {
        Object value = map.get(actualKey);
        if (value instanceof ResourceHolder && ((ResourceHolder)value).isVoid()) {
            map.remove(actualKey);
            if (map.isEmpty()) {
                resources.remove();
            }

            value = null;
        }

        return value;
    }
}

所以

if (savePoint != null) {
    transactionStatus.rollbackToSavepoint(savePoint);
} else {
    transactionStatus.setRollbackOnly();
}

在执行回滚时是同一个Connection.rollback()。

声明式事物

如果仅仅只是提供API,在进行数据库操作时,仍很麻烦,所以Spring还提出了声明式事物,@Transactional注解。

声明式事物demo。

pom.xml 同上

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.demo.spring</groupId>
    <artifactId>spring-tx-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spring.version>4.3.8.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.35</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>4.0.4.RELEASE</version>
        </dependency>
    </dependencies>
</project>

spring-tx.xml

    <?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.demo.spring.*"/>

    <aop:aspectj-autoproxy expose-proxy="true"/>

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

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <constructor-arg name="url" value="jdbc:mysql://192.168.5.104/spring"/>
        <constructor-arg name="username" value="root"/>
        <constructor-arg name="password" value="root"/>
    </bean>

    <tx:annotation-driven transaction-manager="txManager"/>

</beans>

Account.java

package com.demo.spring.service;

import java.io.Serializable;

/**
 * com.demo.spring.service
 *
 * @author Zyy
 * @date 2019/2/14 22:00
 */
public class Account implements Serializable {
    private static final long serialVersionUID = 1L;

    private int id;
    private String accountName;
    private String user;
    private String money;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getAccountName() {
        return accountName;
    }

    public void setAccountName(String accountName) {
        this.accountName = accountName;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getMoney() {
        return money;
    }

    public void setMoney(String money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", accountName='" + accountName + '\'' +
                ", user='" + user + '\'' +
                ", money='" + money + '\'' +
                '}';
    }
}

AccountService.java

package com.demo.spring.service;

import java.util.List;
import java.util.Map;

/**
 * com.demo.spring.service
 *
 * @author Zyy
 * @date 2019/2/14 21:54
 */
public interface AccountService {
    void addAccount(String name, int money);

    int updateAccount(String name, int money);

    List<Map<String, Object>> queryAccount(String name);

}

AccountServiceImpl.java

package com.demo.spring.service;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * com.demo.spring.service
 *
 * @author Zyy
 * @date 2019/2/14 21:58
 */
@Service
public class AccountServiceImpl implements AccountService{
    @Resource
    private JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addAccount(String name, int money) {
        String accountid = new SimpleDateFormat("yyyyMMddhhmmss").format(new Date());
        jdbcTemplate.update("insert into account (accountname,user,money) values (?,?,?)", accountid, name, money);
    }

    @Transactional
    public List<Map<String, Object>> queryAccount(String name) {
        List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from account where user = ?", name);
        return list;
    }

    @Transactional
    public int updateAccount(String name, int money) {
        return jdbcTemplate.update("update account set money = money + ? where user = ?", money, name);
    }
}

AccountServiceTest.java

package com.demo.spring;

import com.demo.spring.service.AccountService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/14 22:15
 */
public class AccountServiceTest {

    @Test
    public void add() {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-tx.xml");
        AccountService accountService = context.getBean(AccountService.class);
        accountService.addAccount("ayang",100);
    }

    @Test
    public void update() {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-tx.xml");
        AccountService accountService = context.getBean(AccountService.class);
        accountService.updateAccount("ayang",110);
    }

    @Test
    public void select() {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-tx.xml");
        AccountService accountService = context.getBean(AccountService.class);
        List<Map<String, Object>> list = accountService.queryAccount("ayang");
        System.out.println(Arrays.toString(list.toArray()));
    }
}

Spring事物默认的传播机制:

@Transactional(propagation = Propagation.REQUIRES_NEW)

下面介绍一下spring事物的传播机制。

Spring事物传播机制
类别 事物传播类型 说明
支持当前事物 PROPAGATION_REQUIRED(必须的) 如果当前没有事物,就新建一个事物,如果已经存在一个事物中,加入到这个事物中。这是最常见的选择。
支持当前事物 PROPAGATION_SUPPORTS(支持) 支持当前事物,如果当前没有事物,就以非事物方式执行。
支持当前事物 PROPAGATION_MANDATORY(强制) 使用当前的事物,如果当前没有事物,就抛出异常。
不支持当前事物 PROPAGATION_REQUIRES_NEW(隔离) 新建事物,如果当前存在事物,把当前事物挂起。
不支持当前事物 PROPAGATION_NOT_SUPPORTED(不支持) 以非事物方式执行操作,如果当前存在事物,就把当前事物挂起。
不支持当前事物 PROPAGATION_NEVER(强制非事物) 以非事物方式执行,如果当前存在事物,则抛出异常。
嵌套事物 PROPAGATION_NESTED(嵌套事物) 如果当前存在事物,则在嵌套事物内执行。如果当前没有事物,则执行与PROPAGATION_REQUIRED类似的操作。
常用的事物传播机制:
  • PROPAGATION_REQUIRED

如果当前没有事物,就新建一个事物,如果已经存在一个事物中,加入到这个事物中这个是默认传播机制。

  • PROPAGATION_NOT_SUPPORTED

以非事物方式执行操作,如果当前存在事物,就把当前事物挂起。可以用于发送提示信息,站内信,邮件提示灯。不属于并且不应该影响主体业务逻辑,即时发送失败也不应该对主题业务逻辑回滚。

  • PROPAGATION_REQUIRED_NEW

新建一个事物,如果存在当前事物,则将事物挂起。总是新启一个事物,这个传播机制适用于不受父类方法事物影响的操作,比如某些业务场景下需要记录业务日志,用于异步反查,那么不管主体业务逻辑是否完成,日志都需要记录下来,不能因为主体业务逻辑报错而丢失日志。

测试:
表结构 服务类 功能描述
user userService 创建用户,并添加账号
account accountService 添加账号

相关代码:

package com.demo.spring;

import com.demo.spring.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/14 22:15
 */
public class UserServiceTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-tx.xml");
        UserService userService = context.getBean(UserService.class);
        userService.addUser("ayang");
    }
}


@Transactional(propagation = Propagation.REQUIRED)
    public void addUser(String name) {
        jdbcTemplate.update("insert into user (name) values(?)", name);
        accountService.addAccount(name, 100);
    }


@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addAccount(String name, int money) {
    String accountid = new SimpleDateFormat("yyyyMMddhhmmss").format(new Date());
    jdbcTemplate.update("insert into account (accountname,user,money) values (?,?,?)", accountid, name, money);
    int i = 1/0;
}

场景:

场景 createUser addAccount(Exception) 结果
1 无事物 required user成功,account失败
2 required 无事物 user失败,account失败
3 required not_supported user失败,account成功
4 required required_new user失败,account失败
5 required(异常移至create末尾) required_new user失败,account成功
6 required(异常移至create末尾),add方法至当前类 required_new user失败,account失败

场景5和6为何会出现不一致,required_new没有起到作用?

spring 声明示事物使用动态代理实现的。

代理模拟:

TransactionalTest.java

com.demo.spring.TransactionalTest   

package com.demo.spring;

import com.demo.spring.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * com.demo.spring
 *
 * @author Zyy
 * @date 2019/2/18 23:05
 */
public class TransactionalTest {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-tx.xml");
        final UserService userService = context.getBean(UserService.class);

        UserService proxyUserService = (UserService) Proxy.newProxyInstance(
                TransactionalTest.class.getClassLoader(), new Class[]{UserService.class}, new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        try {
                            System.out.println("开启事物:" + method.getName());
                            return method.invoke(userService,args);
                        } finally {
                            System.out.println("关闭事物:" + method.getName());
                        }
                    }
                });

        proxyUserService.addUser("ayang");
    }
}

UserServiceImpl.java

package com.demo.spring.service;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * com.demo.spring.service
 *
 * @author Zyy
 * @date 2019/2/14 21:53
 */
@Service
public class UserServiceImpl implements UserService{
    @Resource
    private AccountService accountService;
    @Resource
    private JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.REQUIRED)
    public void addUser(String name) {
        jdbcTemplate.update("insert into user (name) values(?)", name);
        this.addAccount(name, 100);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addAccount(String name, int money) {
        String accountid = new SimpleDateFormat("yyyyMMddhhmmss").format(new Date());
        jdbcTemplate.update("insert into account (accountname,user,money) values (?,?,?)", accountid, name, money);
        //int i = 1/0;
    }

}

结果:

开启事物:addUser
关闭事物 :  addUser

当我们调用addUser方法时,仅打印addUser的事物开启和关闭,并没打印addAccount的事物开启和关闭,所以addAccount的事物的失效的。

原因在于spring 声明示事物使用动态代理实现,而当调用同一个类的方法时,是不会走代理逻辑的,自然事物的配置也会失效。

如果遇到这种情况如何处理?

在配置文件中添加:

<!-- 配置暴露proxy -->
<aop:aspectj-autoproxy expose-proxy="true"/>

在spring xml中配置 暴露proxy 对象,然后在代码中用AopContext.currentProxy() 就可以获当前代理对象,然后基于代理对象调用创建帐户

((UserSerivce) AopContext.currentProxy()).addAccount(name, 10000);

当复现场景6,发现事物的配置又生效了,与场景5结果一致,addUser失败,addAcount成功。

github : https://github.com/zhaoyybalabala/spring-test


欢迎留言交流:)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容

  • 最近再次拜读了spring的源码,对关于spring事物的应用部分加以整理,做如下笔记。如有不正确的地方,欢迎指正...
    默写流年阅读 1,994评论 0 7
  • 什么是事务 事务就是一组操作数据库的动作集合。 动作集合被完整地执行,我们称该事务被提交。动作集合中的某一部分执行...
    超级变换形态阅读 653评论 0 6
  • 这部分的参考文档涉及数据访问和数据访问层和业务或服务层之间的交互。 Spring的综合事务管理支持覆盖很多细节,然...
    竹天亮阅读 1,010评论 0 0
  • 1. TransactionDefinition接口中定义五个隔离级别: ISOLATION_DEFAULT 这是...
    谁在烽烟彼岸阅读 14,836评论 0 3
  • 写这篇文章带着无限的愧疚,加入007已经有半年,这半年里大部分作业都是截止当天才开始写作业。一直都知道继续这样,在...
    Qin薇薇安阅读 181评论 0 0