译 4. 事务管理

1. 打开我们的STS, New————> Import Spring Getting Started Content

2. 输入ma ,选择Managing Transactions

3. 创建一个booking Service

首先,使用BookingService类创建一个基于JDBC的服务,将人员按名称登录到系统中。

src/main/java/hello/BookingService.java

package hello;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class BookingService {

    private final static Logger logger = LoggerFactory.getLogger(BookingService.class);

    private final JdbcTemplate jdbcTemplate;

    public BookingService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Transactional
    public void book(String... persons) {
        for (String person : persons) {
            logger.info("Booking " + person + " in a seat...");
            jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person);
        }
    }

    public List<String> findAllBookings() {
        return jdbcTemplate.query("select FIRST_NAME from BOOKINGS",
                (rs, rowNum) -> rs.getString("FIRST_NAME"));
    }

}
 该代码有一个自动装配的JdbcTemplate,这是一个方便的模板类,用于执行下面代码所需的所有数据库交互。

你也有一个预订方法,旨在预订多个人。 它循环遍历人员列表,并且使用JdbcTemplate将它们插入到BOOKINGS表中。 此方法使用@Transactional进行标记,这意味着任何失败都会导致整个操作回滚到之前的状态,并重新抛出原始异常。 这意味着如果一个人未能被添加,则没有人会被添加到预订中。

您还有一个findAllBookings方法来查询数据库。 从数据库中提取的每一行都被转换为一个字符串,然后组合成一个List。

构建一个应用程序

src/main/java/hello/Application.java

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

@SpringBootApplication是一个方便的注解,它增加了以下所有内容:

  • @Configuration将类标记为应用程序上下文的bean定义的来源。
  • @EnableAutoConfiguration通知Spring Boot根据类路径设置,其他bean和各种属性设置开始添加bean。
  • 通常你会为Spring MVC应用程序添加@EnableWebMvc,但Spring Boot在类路径中看到spring-webmvc时会自动添加。 这将该应用程序标记为Web应用程序并激活关键行为,例如设置DispatcherServlet。
  • @ComponentScan告诉Spring在hello包中查找其他组件,配置和服务,以便找到控制器。

main()方法使用Spring Boot的SpringApplication.run()方法启动应用程序。 你有没有注意到没有一行XML? 没有web.xml文件。 这个Web应用程序是100%纯Java,您不必处理配置任何管道或基础设施。

您的应用程序实际上是零配置。 Spring Boot会检测类路径和h2上的spring-jdbc,并自动为您创建一个DataSource和一个JdbcTemplate。 由于这样的基础架构现在可用并且没有专门的配置,因此还会为您创建一个DataSourceTransactionManager:这是拦截@Transactional批注方法(例如BookingService上的书)的组件。 BookingService通过类路径扫描进行检测。

本指南中演示的另一个Spring Boot功能是在启动时初始化模式的能力

src/main/resources/schema.sql

drop table BOOKINGS if exists;
create table BOOKINGS(ID serial, FIRST_NAME varchar(5) NOT NULL);

还有一个CommandLineRunner注入BookingService并展示各种事务用例。

src/main/java/hello/AppRunner.java

package hello;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

@Component
class AppRunner implements CommandLineRunner {

    private final static Logger logger = LoggerFactory.getLogger(AppRunner.class);

    private final BookingService bookingService;

    public AppRunner(BookingService bookingService) {
        this.bookingService = bookingService;
    }

    @Override
    public void run(String... args) throws Exception {
        bookingService.book("Alice", "Bob", "Carol");
        Assert.isTrue(bookingService.findAllBookings().size() == 3,
                "First booking should work with no problem");
        logger.info("Alice, Bob and Carol have been booked");
        try {
            bookingService.book("Chris", "Samuel");
        } catch (RuntimeException e) {
            logger.info("v--- The following exception is expect because 'Samuel' is too " +
                    "big for the DB ---v");
            logger.error(e.getMessage());
        }

        for (String person : bookingService.findAllBookings()) {
            logger.info("So far, " + person + " is booked.");
        }
        logger.info("You shouldn't see Chris or Samuel. Samuel violated DB constraints, " +
                "and Chris was rolled back in the same TX");
        Assert.isTrue(bookingService.findAllBookings().size() == 3,
                "'Samuel' should have triggered a rollback");

        try {
            bookingService.book("Buddy", null);
        } catch (RuntimeException e) {
            logger.info("v--- The following exception is expect because null is not " +
                    "valid for the DB ---v");
            logger.error(e.getMessage());
        }

        for (String person : bookingService.findAllBookings()) {
            logger.info("So far, " + person + " is booked.");
        }
        logger.info("You shouldn't see Buddy or null. null violated DB constraints, and " +
                "Buddy was rolled back in the same TX");
        Assert.isTrue(bookingService.findAllBookings().size() == 3,
                "'null' should have triggered a rollback");
    }
}

您可以使用Gradle或Maven从命令行运行应用程序。 或者您可以构建一个包含所有必需的依赖项,类和资源的可执行JAR文件,并运行该文件。 这使得在整个开发生命周期内跨越不同环境等,将服务作为应用程序发布,版本化和部署变得非常容易。

我们通过STS生成可执行Jar

生成后可以看到这个文件在target文件夹下

如果您正在使用Gradle,则可以使用./gradlew bootRun运行该应用程序。 或者您可以使用./gradlew构建构建JAR文件。 然后你可以运行JAR文件:

java -jar build/libs/gs-managing-transactions-0.1.0.jar

如果您使用的是Maven,则可以使用./mvnw spring-boot:run来运行该应用程序。 或者,您可以使用./mvnw clean包构建JAR文件。 然后你可以运行JAR文件:

java -jar target/gs-managing-transactions-0.1.0.jar

执行后可以看到回显

 

BOOKINGS表在first_name列上有两个约束:

名称不能超过五个字符。

名称不能为空。

前三个名字是爱丽丝,鲍勃和卡罗尔。 该申请声称有三人被添加到该表中。 如果这样做没有成功,申请就会提前退出。

接下来,另一个预订是为克里斯和塞缪尔完成的。 塞缪尔的名字故意太长,强制插入错误。 交易行为规定克里斯和塞缪尔; 也就是这个事务,应该被回滚。 因此,该表中应该只有三个人,这一说法表明了这一点。

最后,Buddy和null都被预订了。 如输出所示,null也会导致回滚,从而留下相同的三个人。


 源码:点击查看

 恭喜! 您刚刚使用Spring开发了一个包含非侵入事务的简单JDBC应用程序。

posted @ 2018-05-29 17:05  技术宅星云  阅读(296)  评论(0编辑  收藏  举报