tags | projects |
---|---|
This guide walks you through the process of wrapping database operations with non-intrusive transactions.
You’ll build a simple JDBC application wherein you make database operations transactional without having to write specialized JDBC code.
First, use the BookingService
class to create a JDBC-based service that books people into the system by name.
src/main/java/hello/BookingService.java
link:complete/src/main/java/hello/BookingService.java[role=include]
The code has an autowired JdbcTemplate
, a handy template class that does all the database interactions needed by the code below.
You also have a book
method aimed at booking multiple people. It loops through the list of people, and for each person, inserts them into the BOOKINGS
table using the JdbcTemplate
. This method is tagged with @Transactional
, meaning that any failure causes the entire operation to roll back to its previous state, and to re-throw the original exception. This means that none of the people will be added to BOOKINGS
if one person fails to be added.
You also have a findAllBookings
method to query the database. Each row fetched from the database is converted into a String
and then assembled into a List
.
src/main/java/hello/Application.java
link:complete/src/main/java/hello/Application.java[role=include]
@SpringBootApplication
is a convenience annotation that adds all of the following:
-
@Configuration
tags the class as a source of bean definitions for the application context. -
@EnableAutoConfiguration
tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings. -
Normally you would add
@EnableWebMvc
for a Spring MVC app, but Spring Boot adds it automatically when it sees spring-webmvc on the classpath. This flags the application as a web application and activates key behaviors such as setting up aDispatcherServlet
. -
@ComponentScan
tells Spring to look for other components, configurations, and services in the thehello
package, allowing it to find theHelloController
.
The main()
method uses Spring Boot’s SpringApplication.run()
method to launch an application. Did you notice that there wasn’t a single line of XML? No web.xml file either. This web application is 100% pure Java and you didn’t have to deal with configuring any plumbing or infrastructure.
You configure your beans in the Application
configuration class. The bookingService
method wires in an instance of BookingService
.
As shown earlier in this guide, JdbcTemplate
is autowired into BookingService
, meaning you now need to define it in the Application
code:
The jdbcTemplate
method where you create an instance of JdbcTemplate
also contains some DDL to declare the BOOKINGS
table.
Spring Boot automatically creates the needed beans for transactions based on the presence of spring-tx on the classpath.
Note
|
In production systems, database tables are usually declared outside the application. |
You should see the following output:
2014-08-28 10:49:20.935 INFO 24084 --- [ main] hello.Application : Creating tables 2014-08-28 10:49:21.347 INFO 24084 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2014-08-28 10:49:21.389 INFO 24084 --- [ main] hello.Application : Started Application in 1.488 seconds (JVM running for 1.772) 2014-08-28 10:49:21.443 INFO 24084 --- [ main] hello.BookingService : Booking Alice in a seat... 2014-08-28 10:49:21.447 INFO 24084 --- [ main] hello.BookingService : Booking Bob in a seat... 2014-08-28 10:49:21.447 INFO 24084 --- [ main] hello.BookingService : Booking Carol in a seat... 2014-08-28 10:49:21.536 INFO 24084 --- [ main] hello.BookingService : Booking Chris in a seat... 2014-08-28 10:49:21.537 INFO 24084 --- [ main] hello.BookingService : Booking Samuel in a seat... 2014-08-28 10:49:21.545 INFO 24084 --- [ main] o.s.b.f.xml.XmlBeanDefinitionReader : Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml] 2014-08-28 10:49:21.612 INFO 24084 --- [ main] o.s.jdbc.support.SQLErrorCodesFactory : SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase] 2014-08-28 10:49:21.623 INFO 24084 --- [ main] hello.Application : v--- The following exception is expect because 'Samuel' is too big for the DB ---v 2014-08-28 10:49:21.623 ERROR 24084 --- [ main] hello.Application : PreparedStatementCallback; SQL [insert into BOOKINGS(FIRST_NAME) values (?)]; Value too long for column "FIRST_NAME VARCHAR(5) NOT NULL": "'Samuel' (6)"; SQL statement: insert into BOOKINGS(FIRST_NAME) values (?) [22001-176]; nested exception is org.h2.jdbc.JdbcSQLException: Value too long for column "FIRST_NAME VARCHAR(5) NOT NULL": "'Samuel' (6)"; SQL statement: insert into BOOKINGS(FIRST_NAME) values (?) [22001-176] 2014-08-28 10:49:21.661 INFO 24084 --- [ main] hello.Application : So far, Alice is booked. 2014-08-28 10:49:21.661 INFO 24084 --- [ main] hello.Application : So far, Bob is booked. 2014-08-28 10:49:21.662 INFO 24084 --- [ main] hello.Application : So far, Carol is booked. 2014-08-28 10:49:21.662 INFO 24084 --- [ main] hello.Application : You shouldn't see Chris or Samuel. Samuel violated DB constraints, and Chris was rolled back in the same TX 2014-08-28 10:49:21.730 INFO 24084 --- [ main] hello.BookingService : Booking Buddy in a seat... 2014-08-28 10:49:21.731 INFO 24084 --- [ main] hello.BookingService : Booking null in a seat... 2014-08-28 10:49:21.735 INFO 24084 --- [ main] hello.Application : v--- The following exception is expect because null is not valid for the DB ---v 2014-08-28 10:49:21.736 ERROR 24084 --- [ main] hello.Application : PreparedStatementCallback; SQL [insert into BOOKINGS(FIRST_NAME) values (?)]; NULL not allowed for column "FIRST_NAME"; SQL statement: insert into BOOKINGS(FIRST_NAME) values (?) [23502-176]; nested exception is org.h2.jdbc.JdbcSQLException: NULL not allowed for column "FIRST_NAME"; SQL statement: insert into BOOKINGS(FIRST_NAME) values (?) [23502-176] 2014-08-28 10:49:21.771 INFO 24084 --- [ main] hello.Application : So far, Alice is booked. 2014-08-28 10:49:21.772 INFO 24084 --- [ main] hello.Application : So far, Bob is booked. 2014-08-28 10:49:21.772 INFO 24084 --- [ main] hello.Application : So far, Carol is booked. 2014-08-28 10:49:21.772 INFO 24084 --- [ main] hello.Application : You shouldn't see Buddy or null. null violated DB constraints, and Buddy was rolled back in the same TX
The BOOKINGS
table has two constraints on the first_name column:
-
Names cannot be longer than five characters.
-
Names cannot be null.
The first three names inserted are Alice, Bob, and Carol. The application asserts that three people were added to that table. If that had not worked, the application would have exited early.
Next, another booking is done for Chris and Samuel. Samuel’s name is deliberately too long, forcing an insert error. Transactional behavior stipulates that both Chris and Samuel; that is, this transaction, should be rolled back. Thus there should still be only three people in that table, which the assertion demonstrates.
Finally, Buddy and null are booked. As the output shows, null causes a rollback as well, leaving the same three people booked.
Congratulations! You’ve just used Spring to develop a simple JDBC application wrapped with non-intrusive transactions.