译 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将它们插入到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应用程序。