For another use of Hibernate with CockroachDB, see our examples-orms repository.
Step 1. Start CockroachDB
Choose your installation method
You can install a CockroachDB Serverless cluster using either the CockroachDB Cloud Console, a web-based graphical user interface (GUI) tool, or ccloud, a command-line interface (CLI) tool.
Create a free cluster
Note:
Organizations without billing information on file can only create one CockroachDB Basic cluster.
On the Cloud & Regions page, select a cloud provider (GCP or AWS) in the Cloud provider section.
In the Regions section, select a region for the cluster. Refer to CockroachDB Cloud Regions for the regions where CockroachDB Basic clusters can be deployed. To create a multi-region cluster, click Add region and select additional regions.
Click Next: Capacity.
On the Capacity page, select Start for free. Click Next: Finalize.
On the Finalize page, click Create cluster.
Your cluster will be created in a few seconds and the Create SQL user dialog will display.
Create a SQL user
The Create SQL user dialog allows you to create a new SQL user and password.
Enter a username in the SQL user field or use the one provided by default.
Click Generate & save password.
Copy the generated password and save it in a secure location.
Click Next.
Currently, all new SQL users are created with admin privileges. For more information and to change the default settings, see [Manage SQL users on a cluster.
Get the connection information
The Connect to cluster dialog shows information about how to connect to your cluster.
Select Parameters only from the Select option dropdown.
Copy the connection information for each parameter displayed and save it in a secure location.
Follow these steps to create a CockroachDB Serverless cluster using the ccloud CLI tool.
Note:
The ccloud CLI tool is in Preview.
Install ccloud
Choose your OS:
You can install ccloud using either Homebrew or by downloading the binary.
In a PowerShell window, enter the following command to download and extract the ccloud binary and add it to your PATH:
$ErrorActionPreference="Stop";[Net.ServicePointManager]::SecurityProtocol =[Net.SecurityProtocolType]::Tls12;$ProgressPreference='SilentlyContinue';$null= New-Item -Type Directory -Force$env:appdata/ccloud; Invoke-WebRequest -Uri https://binaries.cockroachdb.com/ccloud/ccloud_windows-amd64_0.6.12.zip -OutFile ccloud.zip; Expand-Archive -Force-Path ccloud.zip; Copy-Item -Force ccloud/ccloud.exe -Destination$env:appdata/ccloud;$Env:PATH +=";$env:appdata/ccloud";# We recommend adding ";$env:appdata/ccloud" to the Path variable for your system environment. See https://docs.microsoft.com/powershell/module/microsoft.powershell.core/about/about_environment_variables#saving-changes-to-environment-variables for more information.
Run ccloud quickstart to create a new cluster, create a SQL user, and retrieve the connection string.
The easiest way of getting started with CockroachDB Cloud is to use ccloud quickstart. The ccloud quickstart command guides you through logging in to CockroachDB Cloud, creating a new CockroachDB Basic cluster, and connecting to the new cluster. Run ccloud quickstart and follow the instructions:
ccloud quickstart
The ccloud quickstart command will open a browser window to log you in to CockroachDB Cloud. If you are new to CockroachDB Cloud, you can register using one of the single sign-on (SSO) options, or create a new account using an email address.
The ccloud quickstart command will prompt you for the cluster name, cloud provider, and cloud provider region, then ask if you want to connect to the cluster. Each prompt has default values that you can select, or change if you want a different option.
Select Parameters only then copy the connection parameters displayed and save them in a secure location.
? How would you like to connect? Parameters only
Looking up cluster ID: succeeded
Creating SQL user: succeeded
Success! Created SQL user
name: maxroach
cluster: 37174250-b944-461f-b1c1-3a99edb6af32
Retrieving cluster info: succeeded
Connection parameters
Database: defaultdb
Host: blue-dog-147.6wr.cockroachlabs.cloud
Password: ThisIsNotAGoodPassword
Port: 26257
Username: maxroach
The version of the CockroachDB Hibernate dialect in hibernate.cfg.xml corresponds to a version of CockroachDB. For more information, see Install Client Drivers: Hibernate.
Step 3. Run the code
The sample code in this tutorial (Sample.java) uses Hibernate to map Java methods to SQL operations. The code performs the following operations, which roughly correspond to method calls in the Sample class:
Creates an accounts table as specified by the Account mapping class.
Inserts rows into the table with the addAccounts() method.
Transfers money from one account to another with the transferFunds() method.
Prints out account balances before and after the transfer with the getAccountBalance() method.
In addition, the code shows a pattern for automatically handling transaction retries by wrapping transactions in a higher-order function named runTransaction(). It also includes a method for testing the retry handling logic (Sample.forceRetryLogic()), which will be run if you set the FORCE_RETRY variable to true.
It does all of the above using the practices we recommend for using Hibernate (and the underlying JDBC connection) with CockroachDB, which are listed in the Recommended Practices section below.
The contents of Sample.java:
packagecom.cockroachlabs;importjava.io.Serializable;importjava.math.BigDecimal;importjava.util.Random;importjava.util.function.Function;importjakarta.persistence.Column;importjakarta.persistence.Entity;importjakarta.persistence.Id;importjakarta.persistence.Table;importorg.hibernate.JDBCException;importorg.hibernate.Session;importorg.hibernate.SessionFactory;importorg.hibernate.Transaction;importorg.hibernate.cfg.Configuration;publicclassSampleimplementsSerializable{privatestaticfinalRandomRAND=newRandom();privatestaticfinalbooleanFORCE_RETRY=false;privatestaticfinalStringRETRY_SQL_STATE="40001";privatestaticfinalintMAX_ATTEMPT_COUNT=6;privatestaticFunction<Session,BigDecimal>addAccounts()throwsJDBCException{Function<Session,BigDecimal>f=s->{BigDecimalrv=newBigDecimal(0);try{s.save(newAccount(1,1000));s.save(newAccount(2,250));s.save(newAccount(3,314159));rv=BigDecimal.valueOf(1);System.out.printf("APP: addAccounts() --> %.2f\n",rv);}catch(JDBCExceptione){throwe;}returnrv;};returnf;}privatestaticFunction<Session,BigDecimal>transferFunds(longfromId,longtoId,BigDecimalamount)throwsJDBCException{Function<Session,BigDecimal>f=s->{BigDecimalrv=newBigDecimal(0);try{AccountfromAccount=(Account)s.get(Account.class,fromId);AccounttoAccount=(Account)s.get(Account.class,toId);if(!(amount.compareTo(fromAccount.getBalance())>0)){fromAccount.balance=fromAccount.balance.subtract(amount);toAccount.balance=toAccount.balance.add(amount);s.save(fromAccount);s.save(toAccount);rv=amount;System.out.printf("APP: transferFunds(%d, %d, %.2f) --> %.2f\n",fromId,toId,amount,rv);}}catch(JDBCExceptione){throwe;}returnrv;};returnf;}// Test our retry handling logic if FORCE_RETRY is true. This// method is only used to test the retry logic. It is not// intended for production code.privatestaticFunction<Session,BigDecimal>forceRetryLogic()throwsJDBCException{Function<Session,BigDecimal>f=s->{BigDecimalrv=newBigDecimal(-1);try{System.out.printf("APP: testRetryLogic: BEFORE EXCEPTION\n");s.createNativeQuery("SELECT crdb_internal.force_retry('1s')").executeUpdate();}catch(JDBCExceptione){System.out.printf("APP: testRetryLogic: AFTER EXCEPTION\n");throwe;}returnrv;};returnf;}privatestaticFunction<Session,BigDecimal>getAccountBalance(longid)throwsJDBCException{Function<Session,BigDecimal>f=s->{BigDecimalbalance;try{Accountaccount=s.get(Account.class,id);balance=account.getBalance();System.out.printf("APP: getAccountBalance(%d) --> %.2f\n",id,balance);}catch(JDBCExceptione){throwe;}returnbalance;};returnf;}// Run SQL code in a way that automatically handles the// transaction retry logic so we don't have to duplicate it in// various places.privatestaticBigDecimalrunTransaction(Sessionsession,Function<Session,BigDecimal>fn){BigDecimalrv=newBigDecimal(0);intattemptCount=0;while(attemptCount<MAX_ATTEMPT_COUNT){attemptCount++;if(attemptCount>1){System.out.printf("APP: Entering retry loop again, iteration %d\n",attemptCount);}Transactiontxn=session.beginTransaction();System.out.printf("APP: BEGIN;\n");if(attemptCount==MAX_ATTEMPT_COUNT){Stringerr=String.format("hit max of %s attempts, aborting",MAX_ATTEMPT_COUNT);thrownewRuntimeException(err);}// This block is only used to test the retry logic.// It is not necessary in production code. See also// the method 'testRetryLogic()'.if(FORCE_RETRY){session.createNativeQuery("SELECT now()").list();}try{rv=fn.apply(session);if(!rv.equals(-1)){txn.commit();System.out.printf("APP: COMMIT;\n");break;}}catch(JDBCExceptione){if(RETRY_SQL_STATE.equals(e.getSQLState())){// Since this is a transaction retry error, we// roll back the transaction and sleep a little// before trying again. Each time through the// loop we sleep for a little longer than the last// time (A.K.A. exponential backoff).System.out.printf("APP: retryable exception occurred:\n sql state = [%s]\n message = [%s]\n retry counter = %s\n",e.getSQLState(),e.getMessage(),attemptCount);System.out.printf("APP: ROLLBACK;\n");txn.rollback();intsleepMillis=(int)(Math.pow(2,attemptCount)*100)+RAND.nextInt(100);System.out.printf("APP: Hit 40001 transaction retry error, sleeping %s milliseconds\n",sleepMillis);try{Thread.sleep(sleepMillis);}catch(InterruptedExceptionignored){// no-op}rv=BigDecimal.valueOf(-1);}else{throwe;}}}returnrv;}publicstaticvoidmain(String[]args){// Create a SessionFactory based on our hibernate.cfg.xml configuration// file, which defines how to connect to the database.SessionFactorysessionFactory=newConfiguration().configure("hibernate.cfg.xml").addAnnotatedClass(Account.class).buildSessionFactory();try(Sessionsession=sessionFactory.openSession()){longfromAccountId=1;longtoAccountId=2;BigDecimaltransferAmount=BigDecimal.valueOf(100);if(FORCE_RETRY){System.out.printf("APP: About to test retry logic in 'runTransaction'\n");runTransaction(session,forceRetryLogic());}else{runTransaction(session,addAccounts());BigDecimalfromBalance=runTransaction(session,getAccountBalance(fromAccountId));BigDecimaltoBalance=runTransaction(session,getAccountBalance(toAccountId));if(!fromBalance.equals(-1)&&!toBalance.equals(-1)){// Success!System.out.printf("APP: getAccountBalance(%d) --> %.2f\n",fromAccountId,fromBalance);System.out.printf("APP: getAccountBalance(%d) --> %.2f\n",toAccountId,toBalance);}// Transfer $100 from account 1 to account 2BigDecimaltransferResult=runTransaction(session,transferFunds(fromAccountId,toAccountId,transferAmount));if(!transferResult.equals(-1)){// Success!System.out.printf("APP: transferFunds(%d, %d, %.2f) --> %.2f \n",fromAccountId,toAccountId,transferAmount,transferResult);BigDecimalfromBalanceAfter=runTransaction(session,getAccountBalance(fromAccountId));BigDecimaltoBalanceAfter=runTransaction(session,getAccountBalance(toAccountId));if(!fromBalanceAfter.equals(-1)&&!toBalanceAfter.equals(-1)){// Success!System.out.printf("APP: getAccountBalance(%d) --> %.2f\n",fromAccountId,fromBalanceAfter);System.out.printf("APP: getAccountBalance(%d) --> %.2f\n",toAccountId,toBalanceAfter);}}}}finally{sessionFactory.close();}}}
Update the connection configuration
Open src/main/resources/hibernate.cfg.xml, and set the hibernate.connection.url, hibernate.connection.username, and hibernate.connection.password properties, using the connection information that you obtained from the Cloud Console:
$ cockroach cert create-client max --certs-dir=certs --ca-key=my-safe-directory/ca.key --also-generate-pkcs8-key
The generated PKCS8 key will be named client.max.key.pk8.
Note:
CockroachDB Cloud does not yet support certificate-based user authentication.
Use IMPORT to read in large data sets
If you are trying to get a large data set into CockroachDB all at once (a bulk import), avoid writing client-side code altogether and use the IMPORT statement instead. It is much faster and more efficient than making a series of INSERTs and UPDATEs. It bypasses the SQL layer altogether and writes directly to the storage layer of the database.
This will change batch inserts from insert into foo (col1, col2, col3) values (1,2,3) into insert into foo (col1, col2, col3) values (1,2,3), (4,5,6) this provides 2-3x performance improvement
Retrieve large data sets in chunks using cursors
CockroachDB now supports the PostgreSQL wire-protocol cursors for implicit transactions and explicit transactions executed to completion. This means the PGJDBC driver can use this protocol to stream queries with large result sets. This is much faster than paginating through results in SQL using LIMIT .. OFFSET.
Note that interleaved execution (partial execution of multiple statements within the same connection and transaction) is not supported when Statement.setFetchSize() is used.
What's next?
Read more about using the Hibernate ORM, or check out a more realistic implementation of Hibernate with CockroachDB in our examples-orms repository.
You might also be interested in the following pages: