This tutorial assumes you are running under isolation, which requires client-side retry handling for .
Step 1. Start CockroachDB
Choose whether to run a local cluster or a free CockroachDB Standard cluster.Create a free trial cluster
- . If this is your first CockroachDB Cloud organization, it will be credited with $400 in to get you started.
- On the Get Started page, click Create cluster.
- On the Select a plan page, select Standard.
- On the Cloud & Regions page, select a cloud provider (GCP or AWS).
- In the Regions section, select a region for the cluster. Refer to for the regions where CockroachDB Standard clusters can be deployed. To create a multi-region cluster, click Add region and select additional regions.
- Click Next: Capacity.
- On the Capacity page, keep the at the default value of 2 vCPUs. Click Next: Finalize.
- On the Finalize page, name your cluster. If an active free trial is listed in the right pane, you will not need to add a payment method, though you will need to do this by the to maintain your organization’s clusters. Click Create cluster. Your cluster will be created in a few seconds and the Create SQL user dialog will display.
Set up your cluster connection
Once your cluster is created, the Connect to cluster-name dialog displays. Use the information provided in the dialog to set up your cluster connection for the SQL user that was created by default:-
In your terminal, run the second command from the dialog to create a new
certsdirectory on your local machine and download the CA certificate to that directory:Yourcertfile will be downloaded to~/.postgresql/root.crt. -
Copy the connection string provided, which will be used in the next steps (and to connect to your cluster in the future).
This connection string contains your password, which will be provided only once. If you forget your password, you can reset it by going to the SQL Users page for the cluster, found at
https://cockroachlabs.cloud/cluster/<CLUSTER ID>/users.Where:-
<usernameis the SQL user. By default, this is your CockroachDB Cloud account username. -
<passwordis the password for the SQL user. The password will be shown only once in the Connection info dialog after creating the cluster. -
<cluster-hostnameis the hostname of your CockroachDB Cloud cluster. -
<cluster-idis a unique string used to identify your cluster when downloading the CA certificate. For example,12a3bcde-4fa5-6789-1234-56bc7890d123. You can find these settings in the Connection parameters tab of the Connection info dialog.
-
-
In your terminal, run the second command from the dialog to create a new
certsdirectory on your local machine and download the CA certificate to that directory:Yourcertfile will be downloaded to~/.postgresql/root.crt. -
Copy the connection string provided, which will be used in the next steps (and to connect to your cluster in the future).
This connection string contains your password, which will be provided only once. If you forget your password, you can reset it by going to the SQL Users page for the cluster, found at
https://cockroachlabs.cloud/cluster/<CLUSTER ID>/users.Where:-
<usernameis the SQL user. By default, this is your CockroachDB Cloud account username. -
<passwordis the password for the SQL user. The password will be shown only once in the Connection info dialog after creating the cluster. -
<cluster-hostnameis the hostname of your CockroachDB Cloud cluster. -
<cluster-idis a unique string used to identify your cluster when downloading the CA certificate. For example,12a3bcde-4fa5-6789-1234-56bc7890d123. You can find these settings in the Connection parameters tab of the Connection info dialog.
-
-
In your terminal, run the second command from the dialog to create a new
certsdirectory on your local machine and download the CA certificate to that directory:Yourcertfile will be downloaded to%APPDATA%/.postgresql/root.crt. -
Copy the connection string provided, which will be used in the next steps (and to connect to your cluster in the future).
This connection string contains your password, which will be provided only once. If you forget your password, you can reset it by going to the SQL Users page for the cluster, found at
https://cockroachlabs.cloud/cluster/<CLUSTER ID>/users.Where:-
<usernameis the SQL user. By default, this is your CockroachDB Cloud account username. -
<passwordis the password for the SQL user. The password will be shown only once in the Connection info dialog after creating the cluster. -
<cluster-hostnameis the hostname of your CockroachDB Cloud cluster. -
<cluster-idis a unique string used to identify your cluster when downloading the CA certificate. For example,12a3bcde-4fa5-6789-1234-56bc7890d123. You can find these settings in the Connection parameters tab of the Connection info dialog.
-
Step 2. Create a database and a user
- If you haven’t already, .
-
Start the using the connection string you got from the CockroachDB Cloud Console earlier:
- Enter your SQL user password.
-
In the SQL shell, create the
roach_datadatabase that your application will use:
Step 3. Install JDK
Download and install a Java Development Kit. Spring Boot supports Java versions 8, 11, and 14. In this tutorial, we use JDK 8 from OpenJDK.Step 4. Install Maven
This example application uses Maven to manage all application dependencies. Spring supports Maven versions 3.2 and later. To install Maven on macOS, run the following command:Step 5. Get the application code
To get the application code, download or clone theroach-data repository. The code for the example JDBC application is located under the roach-data-jdbc directory.
(Optional) To recreate the application project structure with the same dependencies as those used by this sample application, you can use Spring initializr with the following settings:
Project
- Maven Project
- Java
- 2.2.6
- Group: io.roach
- Artifact: data
- Name: data
- Package name: io.roach.data
- Packaging: Jar
- Java: 8
- Spring Web
- Spring Data JDBC
- Spring Boot Actuator
- Spring HATEOS
- Liquibase Migration
- PostgreSQL Driver
Step 6. Run the application
Compiling and running the application code will start a web application, initialize theaccounts table in the roach_data database, and submit some requests to the app’s REST API that result in on the running CockroachDB cluster. For details about the application code, see Implementation details.
Open the roach-data/roach-data-jdbc/src/main/resources/application.yml file and edit the datasource settings to connect to your running database cluster:
- and specify the SQL username and password that you created earlier.
- is the name of the CockroachDB Cloud free tier host (e.g.,
free-tier.gcp-us-central1.cockroachlabs.cloud). - is the path to the
cc-ca.crtfile that you downloaded from the CockroachDB Cloud Console. - is the name of your cluster.
If you are using the connection string that you copied from the Connection info modal, your username, password, hostname, and cluster name will be pre-populated.
- If you haven’t already, .
- .
Step 2. Create a database and a user
-
Open a SQL shell to your local cluster using the command:
Where is the full path to the
certsdirectory that you created when setting up the cluster, and is the port at which the cluster is listening for incoming connections. -
In the SQL shell, create the
roach_datadatabase that your application will use: -
Create a SQL user for your app:
Take note of the username and password. You will use it to connect to the database later.
-
Give the user the necessary permissions:
- Exit the shell, and generate a certificate and key for your user by running the following command:
client.{user}.key.pk8.
Step 3. Install JDK
Download and install a Java Development Kit. Spring Boot supports Java versions 8, 11, and 14. In this tutorial, we use JDK 8 from OpenJDK.Step 4. Install Maven
This example application uses Maven to manage all application dependencies. Spring supports Maven versions 3.2 and later. To install Maven on macOS, run the following command:Step 5. Get the application code
To get the application code, download or clone theroach-data repository. The code for the example JDBC application is located under the roach-data-jdbc directory.
(Optional) To recreate the application project structure with the same dependencies as those used by this sample application, you can use Spring initializr with the following settings:
Project
- Maven Project
- Java
- 2.2.6
- Group: io.roach
- Artifact: data
- Name: data
- Package name: io.roach.data
- Packaging: Jar
- Java: 8
- Spring Web
- Spring Data JDBC
- Spring Boot Actuator
- Spring HATEOS
- Liquibase Migration
- PostgreSQL Driver
Step 6. Run the application
Compiling and running the application code will start a web application, initialize theaccounts table in the roach_data database, and submit some requests to the app’s REST API that result in on the running CockroachDB cluster. For details about the application code, see Implementation details.
Open the roach-data/roach-data-jdbc/src/main/resources/application.yml file and edit the datasource settings to connect to your running database cluster:
- is the port number.
- is the full path to the certificates directory containing the authentication certificates that you created earlier.
- and specify the SQL username and password that you created earlier.
roach-data-jdbc project subfolder:
roach-data-jdbc directory, run the application JAR file:
http://localhost:9090/, initializes the account table and changelog tables with Liquibase, and then runs some test operations as requests to the application’s REST API.
For more details about the application code, see Implementation details.
Query the database
Reads
Thehttp://localhost:9090/account endpoint returns information about all accounts in the database. GET requests to these endpoints are executed on the database as SELECT statements.
The following curl command sends a GET request to the endpoint. The json_pp command formats the JSON response.
1 and 2:
http://localhost:9090/transfer endpoint performs transfers between accounts. POST requests to this endpoint are executed as writes (i.e., and ) to the database.
Writes
To make a transfer, send aPOST request to the transfer endpoint, using the arguments specified in the "href” URL (i.e., http://localhost:9090/transfer%7B?fromId,toId,amount).
accounts endpoint to verify that the transfer was successfully completed:
Monitor the application
http://localhost:9090/actuator is the base URL for a number of Spring Boot Actuator endpoints that let you monitor the activity and health of the application.
Implementation details
This section guides you through the different components of the application project in detail.Main application process
JdbcApplication.java defines the application’s main process. It starts a Spring Boot web application, and then submits requests to the app’s REST API that result in database transactions on the CockroachDB cluster.
Here are the contents of JdbcApplication.java:
JdbcApplication class definition declare some important configuration properties for the entire application:
-
@EnableHypermediaSupportenables hypermedia support for resource representation in the application. Currently, the only hypermedia format supported by Spring is HAL, and so thetype = EnableHypermediaSupport.HypermediaType.HAL. For details, see Hypermedia representation. -
@EnableJdbcRepositoriesenables the creation of Spring repositories for data access using Spring Data JDBC. For details, see Spring repositories. -
@EnableAspectJAutoProxyenables the use of@AspectJannotations for declaring aspects. For details, see Transaction management. -
@EnableTransactionManagementenables declarative transaction management in the application. For details, see Transaction management. Note that the@EnableTransactionManagementannotation is passed anorderparameter, which indicates the ordering of advice evaluation when a common join point is reached. For details, see Ordering advice. -
@SpringBootApplicationis a standard configuration annotation used by Spring Boot applications. For details, see Using the @SpringBootApplication on the Spring Boot documentation site.
Schema management
To create and initialize the database schema, the application uses Liquibase.Liquibase changelogs
Liquibase uses changelog files to manage database schema changes. Changelog files include a list of instructions, known as changesets, that are executed against the database in a specified order.resources/db/changelog-master.xml defines the changelog for this application:
sqlFile tag, which tells Liquibase that an external .sql file contains some SQL statements to execute. The file specified by the changeset, resources/db/create.sql, creates the account table:
INSERT statements that initialize the account table with some values.
When the application is started, all of the queries specified by the changesets are executed in the order specified by their changeset tag’s id value. At application startup, Liquibase also creates a table called databasechangelog in the database where it performs changes. This table’s rows log all completed changesets.
To see the completed changesets after starting the application, open a new terminal, start the , and query the databasechangelog table:
Liquibase does not automatically. If a changeset fails at startup, you might need to restart the application manually to complete the changeset.
Liquibase configuration
Typically, Liquibase properties are defined in a separateliquibase.properties file. In this application, the Spring properties file, application.yml, includes properties that enable and configure Liquibase:
contexts property specifies a single Liquibase context (crdb). In order for a changeset to run, its context attribute must match a context set by this property. The context value is crdb in both of the changeset definitions in changelog-master.xml, so both changesets run at application startup.
For simplicity, application.yml only specifies properties for a single Spring profile, with a single set of Liquibase properties. If you want the changelog to include changesets that only run in specific environments (e.g., for debugging and development), you can create a new Spring profile in a separate properties file (e.g., application-dev.yml), and specify a different set of Liquibase properties for that profile. The profile set by the application configuration will automatically use the properties in that profile’s properties file. For information about setting profiles, see the Spring documentation website.
Domain entities
Account.java defines the domain entity for the accounts table. This class is used throughout the application to represent a row of data in the accounts table.
Here are the contents of Account.java:
Hypermedia representation
To represent database objects as HAL+JSON for the REST API, the application extends the Spring HATEOAS module’s RepresentationModel class withAccountModel. Like the Account class, its attributes represent a row of data in the accounts table.
The contents of AccountModel.java:
Spring repositories
To abstract the database layer, Spring applications use theRepository interface, or some subinterface of Repository. This interface maps to a database object, like a table, and its methods map to queries against that object, like a or an statement against a table.
AccountRepository.java defines the main repository for the accounts table:
AccountRepository extends a subinterface of Repository that is provided by Spring for generic CRUD operations called CrudRepository. To support , repositories in other Spring Data modules, like those in Spring Data JPA, usually extend a subinterface of CrudRepository, called PagingAndSortingRepository, that includes pagination and sorting methods. At the time this sample application was created, Spring Data JDBC did not support pagination. As a result, AccountRepository extends a custom repository, called PagedAccountRepository, to provide basic on queries against the accounts table. The AccountRepository methods use the @Query annotation strategy to define queries manually, as strings.
Note that, in addition to having the @Repository annotation, the AccountRepository interface has a @Transactional annotation. When transaction management is enabled in an application (i.e., with @EnableTransactionManagement), Spring automatically wraps all objects with the @Transactional annotation in a proxy that handles calls to the object. For more information, see Understanding the Spring Framework’s Declarative Transaction Implementation on Spring’s documentation website.
@Transactional takes a number of parameters, including a propagation parameter that determines the transaction propagation behavior around an object (i.e., at what point in the stack a transaction starts and ends). This sample application follows the entity-control-boundary (ECB) pattern. As such, the REST service boundaries should determine where a starts and ends rather than the query methods defined in the data access layer. To follow the ECB design pattern, propagation=MANDATORY for AccountRepository, which means that a transaction must already exist in order to call the AccountRepository query methods. In contrast, the @Transactional annotations on the Rest controller entities in the web layer have propagation=REQUIRES_NEW, meaning that a new transaction must be created for each REST request.
The aspects declared in TransactionHintsAspect.java and RetryableTransactionAspect.java further control how @Transactional-annotated components are handled. For more details on control flow and transaction management in the application, see Transaction management.
REST controller
There are several endpoints exposed by the application’s web layer, some of which monitor the health of the application, and some that map to queries executed against the connected database. All of the endpoints served by the application are handled by theAccountController class, which is defined in AccountController.java:
@RestController, AccountController defines the primary web controller component of the application. The AccountController methods define the endpoints, routes, and business logic of REST services for account querying and money transferring. Its attributes include an instantiation of AccountRepository, called accountRepository, that establishes an interface to the accounts table through the data access layer.
As mentioned in the Spring repositories section, the application’s transaction boundaries follow the entity-control-boundary (ECB) pattern, meaning that the web service boundaries of the application determine where a starts and ends. To follow the ECB pattern, the @Transactional annotation on each of the HTTP entities (listAccounts(), getAccount(), and transfer()) has propagation=REQUIRES_NEW. This ensures that each time a REST request is made to an endpoint, a new transaction context is created. For details on how aspects handle control flow and transaction management in the application, see Transaction management.
Transaction management
When transaction management is enabled in an application, Spring automatically wraps all objects annotated with@Transactional in a proxy that handles calls to the object. By default, this proxy starts and closes transactions according to the configured transaction management behavior (e.g., the propagation level).
Using @AspectJ annotations, this sample application extends the default transaction proxy behavior with two other explicitly-defined aspects: TransactionHintsAspect and RetryableTransactionAspect. Methods of these aspects are declared as advice to be executed around method calls annotated with @Transactional.
For more information about transaction management in the app, see the following sections below:
Ordering advice
To determine the order of evaluation when multiple transaction advisors match the same pointcut (in this case, around@Transactional method calls), this application explicitly declares an order of precedence for calling advice.
At the top level of the application, in the main JdbcApplication.java file, the @EnableTransactionManagement annotation is passed an order parameter. This parameter sets the order on the primary transaction advisor to one level of precedence above the lowest level, Ordered.LOWEST_PRECEDENCE. This means that the advisor with the lowest level of precedence is evaluated after the primary transaction advisor (i.e., within the context of an open transaction).
For the two explicitly-defined aspects, TransactionHintsAspect and RetryableTransactionAspect, the @Order annotation is used. Like the order parameter on the @EnableTransactionManagement annotation, @Order takes a value that indicates the precedence of advice. The advisor with the lowest level of precedence is declared in TransactionHintsAspect, the aspect that defines the transaction attributes. RetryableTransactionAspect, the aspect that defines the transaction retry logic, defines the advisor with the highest level of precedence.
For more details about advice ordering, see Advice Ordering on the Spring documentation site.
Transaction attributes
TheTransactionHintsAspect class, declared as an aspect with the @Aspect annotation, declares an advice method that defines the attributes of a transaction. The @Order annotation is passed the lowest level of precedence, Ordered.LOWEST_PRECEDENCE, indicating that this advisor must run after the main transaction advisor, within the context of a transaction. Here are the contents of TransactionHintsAspect.java:
anyTransactionBoundaryOperation method is declared as a pointcut with the @Pointcut annotation. In Spring, pointcut declarations must include an expression to determine where join points occur in the application control flow. To help define these expressions, Spring supports a set of designators. The application uses two of them here: execution, which matches method execution joint points (i.e., defines a joint point when a specific method is executed, in this case, any method in the io.roach. namespace), and @annotation, which limits the matches to methods with a specific annotation, in this case @Transactional.
setTransactionAttributes sets the transaction attributes in the form of advice. Spring supports several different annotations to declare advice. The @Around annotation allows an advice method to work before and after the anyTransactionBoundaryOperation(transactional) join point. It also allows the advice method to call the next matching advisor with the ProceedingJoinPoint.proceed(); method.
On verifying that the transaction is active (using TransactionSynchronizationManager.isActualTransactionActive()), the advice using methods of the JdbcTemplate object declared at the top of the TransactionHintsAspect class definition. These session variables (application_name, statement_timeout, and transaction_read_only) set for the query to “roach-data”, the time allowed for the statement to execute before timing out to 1000 milliseconds (i.e., 1 second), and the as either READ ONLY or READ WRITE.
Transaction retries
SERIALIZABLE transactions may require if they experience deadlock or that cannot be resolved without allowing anomalies.
In this application, transaction retry logic is written into the methods of the RetryableTransactionAspect class. This class is declared an aspect with the @Aspect annotation. The @Order annotation on this aspect class is passed Ordered.LOWEST_PRECEDENCE-2, a level of precedence above the primary transaction advisor. This indicates that the transaction retry advisor must run outside the context of a transaction. Here are the contents of RetryableTransactionAspect.java:
anyTransactionBoundaryOperation pointcut definition is identical to the one declared in TransactionHintsAspect. The execution designator matches all methods in the io.roach. namespace, and the @annotation designator limits the matches to methods with the @Transactional annotation.
retryableOperation handles the application retry logic in the form of advice. The @Around annotation allows the advice method to work before and after an anyTransactionBoundaryOperation(transactional) join point. It also allows the advice method to call the next matching advisor.
retryableOperation first verifies that there is no active transaction. It then increments the retry count and attempts to proceed to the next advice method with the ProceedingJoinPoint.proceed() method. If the underlying methods (i.e., the primary transaction advisor’s methods and the annotated query methods) succeed, the transaction has been successfully committed to the database. The results are then returned and the application flow continues. If a failure in the underlying layers occurs due to a transient error (TransientDataAccessException or TransactionSystemException), then the transaction is retried. The time between each retry grows with each retry until the maximum number of retries is reached.
See also
Spring documentation:- Spring Boot website
- Spring Framework Overview
- Spring Core documentation
- Data Access with JDBC
- Spring Web MVC

