[BAEL-4120] Read-only transactions samples (#11841)
* [BAEL-4120] Read-only transactions samples * Extract query runner jobs logic * Apply test name standards * Fix code standards * Use pom properties to define versions * Rename main package * Rename generics * Code clean up * Code clean up on lambdas * Add debug logs * Fix format issues * Rename entity
This commit is contained in:
		
							parent
							
								
									973c276d7b
								
							
						
					
					
						commit
						ac5dbbc5da
					
				| @ -52,6 +52,7 @@ | ||||
|         <module>persistence-libraries</module> | ||||
|         <module>querydsl</module> | ||||
|         <module>r2dbc</module> | ||||
|         <module>read-only-transactions</module> | ||||
|         <module>redis</module> | ||||
|         <!-- <module>sirix</module> --> <!-- We haven't upgraded to java 11. Fixing in BAEL-10841 --> | ||||
|         <module>solr</module> | ||||
|  | ||||
							
								
								
									
										8
									
								
								persistence-modules/read-only-transactions/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								persistence-modules/read-only-transactions/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| ### Relevant Articles: | ||||
| - | ||||
| 
 | ||||
| ### Instructions | ||||
| To run the `com.baeldung.read_only_transactions.TransactionSetupIntegrationTest` first follow the steps described next: | ||||
| - run the command `docker-compose -f docker-compose-mysql.yml up` | ||||
| - Open a SQL client of your preference and execute the `create.sql` script. | ||||
| - You can check the mysql logs using `tail -f mysql/${name of de log file created}.log` | ||||
							
								
								
									
										43
									
								
								persistence-modules/read-only-transactions/create.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								persistence-modules/read-only-transactions/create.sql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| create table book ( | ||||
|     id bigint(20) AUTO_INCREMENT primary key, | ||||
|     name varchar(255) not null, | ||||
|     uuid varchar(40) | ||||
| ); | ||||
| 
 | ||||
| 
 | ||||
| DELIMITER ;; | ||||
| DROP PROCEDURE IF EXISTS populate; | ||||
| create procedure populate() | ||||
| BEGIN | ||||
|     SET @name1='Josh purchase'; | ||||
|     SET @name2='Henry purchase'; | ||||
|     SET @name3='Betty purchase'; | ||||
|     SET @name4='Kate purchase'; | ||||
|     SET @name5='Mari purchase'; | ||||
|     SET @name=''; | ||||
|     SET @counter=0; | ||||
| 
 | ||||
|     START TRANSACTION; | ||||
| 
 | ||||
|     while @counter < 1000000 do | ||||
|         SET @name = case | ||||
|                     when MOD(@counter, 5) = 0 THEN @name5 | ||||
|                     when MOD(@counter, 3) = 0 THEN @name3 | ||||
|                     when MOD(@counter, 4) = 0 THEN @name4 | ||||
|                     when MOD(@counter, 2) = 0 THEN @name2 | ||||
|                     else @name1 | ||||
|             end; | ||||
| 
 | ||||
|         insert into book(name, uuid) values(@name, uuid()); | ||||
|         SET @counter=@counter+1; | ||||
|     end while; | ||||
| 
 | ||||
|     COMMIT; | ||||
| 
 | ||||
| END;; | ||||
| 
 | ||||
| DELIMITER ; | ||||
| 
 | ||||
| CALL populate(); | ||||
| 
 | ||||
| 
 | ||||
| @ -0,0 +1,15 @@ | ||||
| version: "3.9"  # optional since v1.27.0 | ||||
| services: | ||||
|   mysql: | ||||
|     build: . | ||||
|     restart: always | ||||
|     ports: | ||||
|       - "3306:3306" | ||||
|     environment: | ||||
|       MYSQL_PASSWORD: "baeldung" | ||||
|       MYSQL_USER: "baeldung" | ||||
|       MYSQL_DATABASE: "baeldung" | ||||
|       MYSQL_ROOT_PASSWORD: "baeldung" | ||||
|     command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --innodb_buffer_pool_size=3G --innodb_adaptive_hash_index=off --query_cache_size=0 --query_cache_type=0 --log_output=FILE --general_log=1 | ||||
|     volumes: | ||||
|       - ./mysql:/var/lib/mysql/ | ||||
							
								
								
									
										89
									
								
								persistence-modules/read-only-transactions/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								persistence-modules/read-only-transactions/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | ||||
| <?xml version="1.0"?> | ||||
| <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          xmlns="http://maven.apache.org/POM/4.0.0" | ||||
|          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
| 
 | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|     <artifactId>read-only-transactions</artifactId> | ||||
|     <version>0.0.1-SNAPSHOT</version> | ||||
|     <name>read-only-transactions</name> | ||||
| 
 | ||||
|     <parent> | ||||
|         <groupId>com.baeldung</groupId> | ||||
|         <artifactId>persistence-modules</artifactId> | ||||
|         <version>1.0.0-SNAPSHOT</version> | ||||
|     </parent> | ||||
| 
 | ||||
|     <dependencies> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.boot</groupId> | ||||
|             <artifactId>spring-boot-starter-data-jpa</artifactId> | ||||
|             <version>${spring-boot.version}</version> | ||||
|             <exclusions> | ||||
|                 <exclusion> | ||||
|                     <groupId>com.zaxxer</groupId> | ||||
|                     <artifactId>HikariCP</artifactId> | ||||
|                 </exclusion> | ||||
|             </exclusions> | ||||
|         </dependency> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.boot</groupId> | ||||
|             <artifactId>spring-boot-starter-test</artifactId> | ||||
|             <version>${spring-boot.version}</version> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>org.springframework</groupId> | ||||
|             <artifactId>spring-test</artifactId> | ||||
|             <version>${spring-test.version}</version> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>mysql</groupId> | ||||
|             <artifactId>mysql-connector-java</artifactId> | ||||
|             <version>${mysql.version}</version> | ||||
|         </dependency> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>org.hibernate</groupId> | ||||
|             <artifactId>hibernate-core</artifactId> | ||||
|             <version>${hibernate.version}</version> | ||||
|         </dependency> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>com.zaxxer</groupId> | ||||
|             <artifactId>HikariCP</artifactId> | ||||
|             <version>${hikari.version}</version> | ||||
|         </dependency> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>org.junit.jupiter</groupId> | ||||
|             <artifactId>junit-jupiter-engine</artifactId> | ||||
|             <version>${junit-jupiter.version}</version> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
| 
 | ||||
|         <dependency> | ||||
|             <groupId>com.h2database</groupId> | ||||
|             <artifactId>h2</artifactId> | ||||
|             <version>${h2.version}</version> | ||||
|             <scope>runtime</scope> | ||||
|         </dependency> | ||||
| 
 | ||||
|     </dependencies> | ||||
| 
 | ||||
| 
 | ||||
|     <properties> | ||||
|         <mysql.version>8.0.21</mysql.version> | ||||
|         <hikari.version>4.0.3</hikari.version> | ||||
|         <hibernate.version>5.6.1.Final</hibernate.version> | ||||
|         <spring-boot.version>2.6.1</spring-boot.version> | ||||
|         <spring-test.version>5.3.13</spring-test.version> | ||||
|         <junit-jupiter.version>5.8.2</junit-jupiter.version> | ||||
|         <h2.version>1.4.200</h2.version> | ||||
|     </properties> | ||||
| </project> | ||||
| @ -0,0 +1,44 @@ | ||||
| package com.baeldung.readonlytransactions.h2; | ||||
| 
 | ||||
| import javax.persistence.Entity; | ||||
| import javax.persistence.GeneratedValue; | ||||
| import javax.persistence.GenerationType; | ||||
| import javax.persistence.Id; | ||||
| import javax.persistence.Table; | ||||
| 
 | ||||
| @Entity | ||||
| @Table(name = "book") | ||||
| public class Book { | ||||
| 
 | ||||
|     @Id | ||||
|     @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||
|     private Long id; | ||||
| 
 | ||||
|     private String name; | ||||
| 
 | ||||
|     private String uuid; | ||||
| 
 | ||||
|     public Long getId() { | ||||
|         return id; | ||||
|     } | ||||
| 
 | ||||
|     public void setId(Long id) { | ||||
|         this.id = id; | ||||
|     } | ||||
| 
 | ||||
|     public String getName() { | ||||
|         return name; | ||||
|     } | ||||
| 
 | ||||
|     public void setName(String name) { | ||||
|         this.name = name; | ||||
|     } | ||||
| 
 | ||||
|     public String getUuid() { | ||||
|         return uuid; | ||||
|     } | ||||
| 
 | ||||
|     public void setUuid(String uuid) { | ||||
|         this.uuid = uuid; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| package com.baeldung.readonlytransactions.h2; | ||||
| 
 | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.beans.factory.annotation.Qualifier; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
| 
 | ||||
| import javax.persistence.EntityManagerFactory; | ||||
| 
 | ||||
| @Service | ||||
| public class BookService { | ||||
| 
 | ||||
|     private EntityManagerFactory entityManagerFactory; | ||||
| 
 | ||||
|     public BookService(@Autowired @Qualifier("h2EntityManagerFactory") EntityManagerFactory entityManagerFactory) { | ||||
|         this.entityManagerFactory = entityManagerFactory; | ||||
|     } | ||||
| 
 | ||||
|     @Transactional(readOnly = true) | ||||
|     public Book getBookById(long id) { | ||||
|         return entityManagerFactory.createEntityManager() | ||||
|             .find(Book.class, id); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,54 @@ | ||||
| package com.baeldung.readonlytransactions.h2; | ||||
| 
 | ||||
| import org.springframework.beans.factory.annotation.Qualifier; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; | ||||
| import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; | ||||
| 
 | ||||
| import com.zaxxer.hikari.HikariConfig; | ||||
| import com.zaxxer.hikari.HikariDataSource; | ||||
| 
 | ||||
| import java.util.Properties; | ||||
| 
 | ||||
| import javax.persistence.EntityManagerFactory; | ||||
| import javax.sql.DataSource; | ||||
| 
 | ||||
| @Configuration | ||||
| public class Config { | ||||
| 
 | ||||
|     @Bean("h2DataSource") | ||||
|     public DataSource dataSource() { | ||||
|         HikariConfig config = new HikariConfig(); | ||||
|         config.setJdbcUrl("jdbc:h2:mem:mydb"); | ||||
|         config.setUsername("sa"); | ||||
|         config.setPassword(""); | ||||
|         config.setDriverClassName("org.h2.Driver"); | ||||
|         return new HikariDataSource(config); | ||||
|     } | ||||
| 
 | ||||
|     @Bean("h2EntityManagerFactory") | ||||
|     public EntityManagerFactory entityManagerFactory(@Qualifier("h2DataSource") DataSource dataSource) { | ||||
|         HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); | ||||
|         vendorAdapter.setGenerateDdl(false); | ||||
| 
 | ||||
|         LocalContainerEntityManagerFactoryBean managerFactoryBean = new LocalContainerEntityManagerFactoryBean(); | ||||
|         managerFactoryBean.setJpaVendorAdapter(vendorAdapter); | ||||
|         managerFactoryBean.setPackagesToScan(Config.class.getPackage() | ||||
|             .getName()); | ||||
|         managerFactoryBean.setDataSource(dataSource); | ||||
| 
 | ||||
|         Properties properties = new Properties(); | ||||
| 
 | ||||
|         properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); | ||||
|         properties.setProperty("hibernate.hbm2ddl.auto", "create-drop"); | ||||
| 
 | ||||
|         properties.setProperty("hibernate.show_sql", "true"); | ||||
|         properties.setProperty("hibernate.format_sql", "true"); | ||||
| 
 | ||||
|         managerFactoryBean.setJpaProperties(properties); | ||||
|         managerFactoryBean.afterPropertiesSet(); | ||||
| 
 | ||||
|         return managerFactoryBean.getObject(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| package com.baeldung.readonlytransactions.h2; | ||||
| 
 | ||||
| import org.springframework.beans.factory.annotation.Qualifier; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.orm.jpa.JpaTransactionManager; | ||||
| import org.springframework.transaction.PlatformTransactionManager; | ||||
| import org.springframework.transaction.annotation.EnableTransactionManagement; | ||||
| 
 | ||||
| import javax.persistence.EntityManagerFactory; | ||||
| 
 | ||||
| @Configuration | ||||
| @EnableTransactionManagement | ||||
| public class TransactionConfig { | ||||
| 
 | ||||
|     @Bean | ||||
|     public PlatformTransactionManager transactionManager(@Qualifier("h2EntityManagerFactory") EntityManagerFactory entityManagerFactory) { | ||||
|         return new JpaTransactionManager(entityManagerFactory); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,29 @@ | ||||
| package com.baeldung.readonlytransactions.mysql.dao; | ||||
| 
 | ||||
| import com.baeldung.readonlytransactions.utils.ExecutorUtils; | ||||
| 
 | ||||
| import java.util.concurrent.ExecutorService; | ||||
| import java.util.concurrent.Executors; | ||||
| import java.util.concurrent.ScheduledExecutorService; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| import java.util.concurrent.atomic.AtomicLong; | ||||
| import java.util.function.Consumer; | ||||
| 
 | ||||
| public abstract class BaseRepo { | ||||
| 
 | ||||
|     protected long execQuery(Consumer<AtomicLong> function) { | ||||
|         AtomicLong count = new AtomicLong(0); | ||||
| 
 | ||||
|         ExecutorService executor = ExecutorUtils.createExecutor(10, 10); | ||||
|         ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); | ||||
| 
 | ||||
|         scheduler.schedule(executor::shutdownNow, 5L, TimeUnit.SECONDS); | ||||
|         scheduler.shutdown(); | ||||
| 
 | ||||
|         while (!executor.isShutdown()) { | ||||
|             executor.execute(() -> function.accept(count)); | ||||
|         } | ||||
| 
 | ||||
|         return count.get(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,36 @@ | ||||
| package com.baeldung.readonlytransactions.mysql.dao; | ||||
| 
 | ||||
| import org.hibernate.Session; | ||||
| 
 | ||||
| import com.baeldung.readonlytransactions.mysql.entities.Book; | ||||
| 
 | ||||
| import java.util.SplittableRandom; | ||||
| import java.util.concurrent.atomic.AtomicLong; | ||||
| 
 | ||||
| import javax.persistence.EntityManager; | ||||
| import javax.persistence.EntityManagerFactory; | ||||
| import javax.persistence.Persistence; | ||||
| 
 | ||||
| public class MyRepoJPA extends BaseRepo { | ||||
| 
 | ||||
|     private EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("jpa-unit"); | ||||
|     private SplittableRandom random = new SplittableRandom(); | ||||
| 
 | ||||
|     public long runQuery() { | ||||
|         return execQuery(this::runSql); | ||||
|     } | ||||
| 
 | ||||
|     private void runSql(AtomicLong count) { | ||||
|         if (Thread.interrupted()) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         EntityManager entityManager = entityManagerFactory.createEntityManager(); | ||||
|         Session session = entityManager.unwrap(Session.class); | ||||
|         session.setDefaultReadOnly(true); | ||||
|         entityManager.find(Book.class, 1L + random.nextLong(0, 1000000)); | ||||
|         count.incrementAndGet(); | ||||
|         entityManager.clear(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,71 @@ | ||||
| package com.baeldung.readonlytransactions.mysql.dao; | ||||
| 
 | ||||
| import com.zaxxer.hikari.HikariConfig; | ||||
| import com.zaxxer.hikari.HikariDataSource; | ||||
| 
 | ||||
| import java.sql.Connection; | ||||
| import java.sql.PreparedStatement; | ||||
| import java.sql.ResultSet; | ||||
| import java.sql.SQLException; | ||||
| import java.util.SplittableRandom; | ||||
| import java.util.concurrent.atomic.AtomicLong; | ||||
| 
 | ||||
| public class MyRepoJdbc extends BaseRepo { | ||||
| 
 | ||||
|     static { | ||||
|         try { | ||||
|             Class.forName("com.mysql.cj.jdbc.Driver"); | ||||
|         } catch (ClassNotFoundException e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private HikariDataSource ds; | ||||
|     private SplittableRandom random = new SplittableRandom(); | ||||
| 
 | ||||
|     public MyRepoJdbc(boolean readOnly, boolean autocommit) { | ||||
|         HikariConfig config = new HikariConfig(); | ||||
|         config.setJdbcUrl("jdbc:mysql://localhost/baeldung?useUnicode=true&characterEncoding=UTF-8"); | ||||
|         config.setUsername("baeldung"); | ||||
|         config.setPassword("baeldung"); | ||||
|         config.setReadOnly(readOnly); | ||||
|         config.setAutoCommit(autocommit); | ||||
|         ds = new HikariDataSource(config); | ||||
|     } | ||||
| 
 | ||||
|     private Connection getConnection() throws SQLException { | ||||
|         return ds.getConnection(); | ||||
|     } | ||||
| 
 | ||||
|     public long runQuery(Boolean autoCommit, Boolean readOnly) { | ||||
|         try { | ||||
|             return execQuery(count -> runSql(count, autoCommit, readOnly)); | ||||
|         } finally { | ||||
|             ds.close(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void runSql(AtomicLong count, Boolean autoCommit, Boolean readOnly) { | ||||
|         if (Thread.interrupted()) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         try (Connection connect = getConnection(); PreparedStatement statement = connect.prepareStatement("select * from transactions where id = ?")) { | ||||
|             if (autoCommit != null) | ||||
|                 connect.setAutoCommit(autoCommit); | ||||
| 
 | ||||
|             if (readOnly != null) | ||||
|                 connect.setReadOnly(readOnly); | ||||
| 
 | ||||
|             statement.setLong(1, 1L + random.nextLong(0, 100000)); | ||||
|             ResultSet resultSet = statement.executeQuery(); | ||||
| 
 | ||||
|             if (autoCommit != null && !autoCommit) | ||||
|                 connect.commit(); | ||||
| 
 | ||||
|             count.incrementAndGet(); | ||||
|             resultSet.close(); | ||||
|         } catch (Exception ignored) { | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| package com.baeldung.readonlytransactions.mysql.dao; | ||||
| 
 | ||||
| import com.baeldung.readonlytransactions.mysql.spring.repositories.BookRepository; | ||||
| 
 | ||||
| import java.util.SplittableRandom; | ||||
| 
 | ||||
| public class MyRepoSpring extends BaseRepo { | ||||
| 
 | ||||
|     private SplittableRandom random = new SplittableRandom(); | ||||
|     private BookRepository repository; | ||||
| 
 | ||||
|     public MyRepoSpring(BookRepository repository) { | ||||
|         this.repository = repository; | ||||
|     } | ||||
| 
 | ||||
|     public long runQuery() { | ||||
|         return execQuery(count -> { | ||||
|             repository.get(1L + random.nextLong(0, 1000000)); | ||||
|             count.incrementAndGet(); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,44 @@ | ||||
| package com.baeldung.readonlytransactions.mysql.entities; | ||||
| 
 | ||||
| import javax.persistence.Entity; | ||||
| import javax.persistence.GeneratedValue; | ||||
| import javax.persistence.GenerationType; | ||||
| import javax.persistence.Id; | ||||
| import javax.persistence.Table; | ||||
| 
 | ||||
| @Entity | ||||
| @Table(name = "book") | ||||
| public class Book { | ||||
| 
 | ||||
|     @Id | ||||
|     @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||
|     private Long id; | ||||
| 
 | ||||
|     private String name; | ||||
| 
 | ||||
|     private String uuid; | ||||
| 
 | ||||
|     public Long getId() { | ||||
|         return id; | ||||
|     } | ||||
| 
 | ||||
|     public void setId(Long id) { | ||||
|         this.id = id; | ||||
|     } | ||||
| 
 | ||||
|     public String getName() { | ||||
|         return name; | ||||
|     } | ||||
| 
 | ||||
|     public void setName(String name) { | ||||
|         this.name = name; | ||||
|     } | ||||
| 
 | ||||
|     public String getUuid() { | ||||
|         return uuid; | ||||
|     } | ||||
| 
 | ||||
|     public void setUuid(String uuid) { | ||||
|         this.uuid = uuid; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,77 @@ | ||||
| package com.baeldung.readonlytransactions.mysql.spring; | ||||
| 
 | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.context.annotation.EnableAspectJAutoProxy; | ||||
| import org.springframework.data.jpa.repository.config.EnableJpaRepositories; | ||||
| import org.springframework.orm.jpa.JpaTransactionManager; | ||||
| import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; | ||||
| import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; | ||||
| import org.springframework.transaction.PlatformTransactionManager; | ||||
| import org.springframework.transaction.annotation.EnableTransactionManagement; | ||||
| 
 | ||||
| import com.baeldung.readonlytransactions.mysql.dao.MyRepoSpring; | ||||
| import com.baeldung.readonlytransactions.mysql.spring.entities.BookEntity; | ||||
| import com.baeldung.readonlytransactions.mysql.spring.repositories.BookRepository; | ||||
| import com.zaxxer.hikari.HikariConfig; | ||||
| import com.zaxxer.hikari.HikariDataSource; | ||||
| 
 | ||||
| import java.util.Properties; | ||||
| 
 | ||||
| import javax.persistence.EntityManagerFactory; | ||||
| import javax.sql.DataSource; | ||||
| 
 | ||||
| @Configuration | ||||
| @EnableJpaRepositories(basePackageClasses = Config.class, enableDefaultTransactions = false) | ||||
| @EnableTransactionManagement | ||||
| @EnableAspectJAutoProxy | ||||
| public class Config { | ||||
| 
 | ||||
|     @Bean | ||||
|     public MyRepoSpring repoSpring(BookRepository repository) { | ||||
|         return new MyRepoSpring(repository); | ||||
|     } | ||||
| 
 | ||||
|     private DataSource dataSource(boolean readOnly, boolean isAutoCommit) { | ||||
|         HikariConfig config = new HikariConfig(); | ||||
|         config.setJdbcUrl("jdbc:mysql://localhost/baeldung?useUnicode=true&characterEncoding=UTF-8"); | ||||
|         config.setUsername("baeldung"); | ||||
|         config.setPassword("baeldung"); | ||||
|         config.setReadOnly(readOnly); | ||||
|         config.setAutoCommit(isAutoCommit); | ||||
|         return new HikariDataSource(config); | ||||
|     } | ||||
| 
 | ||||
|     @Bean | ||||
|     public DataSource dataSource() { | ||||
|         return new RoutingDS(dataSource(false, false), dataSource(true, true)); | ||||
|     } | ||||
| 
 | ||||
|     @Bean | ||||
|     public EntityManagerFactory entityManagerFactory(DataSource dataSource) { | ||||
|         HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); | ||||
|         vendorAdapter.setGenerateDdl(false); | ||||
| 
 | ||||
|         LocalContainerEntityManagerFactoryBean managerFactoryBean = new LocalContainerEntityManagerFactoryBean(); | ||||
|         managerFactoryBean.setJpaVendorAdapter(vendorAdapter); | ||||
|         managerFactoryBean.setPackagesToScan(BookEntity.class.getPackage() | ||||
|             .getName()); | ||||
|         managerFactoryBean.setDataSource(dataSource); | ||||
| 
 | ||||
|         Properties properties = new Properties(); | ||||
| 
 | ||||
|         properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect"); | ||||
|         properties.setProperty("hibernate.hbm2ddl.auto", "validate"); | ||||
| 
 | ||||
|         managerFactoryBean.setJpaProperties(properties); | ||||
|         managerFactoryBean.afterPropertiesSet(); | ||||
| 
 | ||||
|         return managerFactoryBean.getObject(); | ||||
|     } | ||||
| 
 | ||||
|     @Bean | ||||
|     public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { | ||||
|         return new JpaTransactionManager(entityManagerFactory); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| package com.baeldung.readonlytransactions.mysql.spring; | ||||
| 
 | ||||
| import java.util.concurrent.atomic.AtomicInteger; | ||||
| 
 | ||||
| public class ReadOnlyContext { | ||||
| 
 | ||||
|     private static final ThreadLocal<AtomicInteger> READ_ONLY_LEVEL = ThreadLocal.withInitial(() -> new AtomicInteger(0)); | ||||
| 
 | ||||
|     private ReadOnlyContext() { | ||||
|     } | ||||
| 
 | ||||
|     public static boolean isReadOnly() { | ||||
|         return READ_ONLY_LEVEL.get() | ||||
|             .get() > 0; | ||||
|     } | ||||
| 
 | ||||
|     public static void enter() { | ||||
|         READ_ONLY_LEVEL.get() | ||||
|             .incrementAndGet(); | ||||
|     } | ||||
| 
 | ||||
|     public static void exit() { | ||||
|         READ_ONLY_LEVEL.get() | ||||
|             .decrementAndGet(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,29 @@ | ||||
| package com.baeldung.readonlytransactions.mysql.spring; | ||||
| 
 | ||||
| import org.aspectj.lang.ProceedingJoinPoint; | ||||
| import org.aspectj.lang.annotation.Around; | ||||
| import org.aspectj.lang.annotation.Aspect; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.stereotype.Component; | ||||
| 
 | ||||
| @Aspect | ||||
| @Component | ||||
| public class ReadOnlyInterception { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(ReadOnlyInterception.class); | ||||
| 
 | ||||
|     @Around("@annotation(com.baeldung.readonlytransactions.mysql.spring.ReaderDS)") | ||||
|     public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable { | ||||
|         try { | ||||
|             ReadOnlyContext.enter(); | ||||
|             //Debug data source switch | ||||
|             logger.debug("-----------------------------Entering read only zone-----------------------------"); | ||||
|             return joinPoint.proceed(); | ||||
|         } finally { | ||||
|             logger.debug("-----------------------------Leaving read only zone------------------------------"); | ||||
|             ReadOnlyContext.exit(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| package com.baeldung.readonlytransactions.mysql.spring; | ||||
| 
 | ||||
| import java.lang.annotation.Inherited; | ||||
| import java.lang.annotation.Retention; | ||||
| import java.lang.annotation.RetentionPolicy; | ||||
| 
 | ||||
| @Inherited | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| public @interface ReaderDS { | ||||
| } | ||||
| @ -0,0 +1,35 @@ | ||||
| package com.baeldung.readonlytransactions.mysql.spring; | ||||
| 
 | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; | ||||
| 
 | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import javax.sql.DataSource; | ||||
| 
 | ||||
| public class RoutingDS extends AbstractRoutingDataSource { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(RoutingDS.class); | ||||
| 
 | ||||
|     RoutingDS(DataSource writer, DataSource reader) { | ||||
| 
 | ||||
|         Map<Object, Object> dataSources = new HashMap<>(); | ||||
|         dataSources.put("writer", writer); | ||||
|         dataSources.put("reader", reader); | ||||
| 
 | ||||
|         setTargetDataSources(dataSources); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected Object determineCurrentLookupKey() { | ||||
|         String dataSourceMode = ReadOnlyContext.isReadOnly() ? "reader" : "writer"; | ||||
| 
 | ||||
|         // Testing data source switch | ||||
|         logger.debug("-----------------------------Datasource: {} ---------------------------------", dataSourceMode); | ||||
| 
 | ||||
|         return dataSourceMode; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,45 @@ | ||||
| package com.baeldung.readonlytransactions.mysql.spring.entities; | ||||
| 
 | ||||
| import javax.persistence.Entity; | ||||
| import javax.persistence.GeneratedValue; | ||||
| import javax.persistence.GenerationType; | ||||
| import javax.persistence.Id; | ||||
| import javax.persistence.Table; | ||||
| 
 | ||||
| @Entity | ||||
| @Table(name = "book") | ||||
| public class BookEntity { | ||||
| 
 | ||||
|     @Id | ||||
|     @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||
|     private Long id; | ||||
| 
 | ||||
|     private String name; | ||||
| 
 | ||||
|     private String uuid; | ||||
| 
 | ||||
|     public Long getId() { | ||||
|         return id; | ||||
|     } | ||||
| 
 | ||||
|     public void setId(Long id) { | ||||
|         this.id = id; | ||||
|     } | ||||
| 
 | ||||
|     public String getName() { | ||||
|         return name; | ||||
|     } | ||||
| 
 | ||||
|     public void setName(String name) { | ||||
|         this.name = name; | ||||
|     } | ||||
| 
 | ||||
|     public String getUuid() { | ||||
|         return uuid; | ||||
|     } | ||||
| 
 | ||||
|     public void setUuid(String uuid) { | ||||
|         this.uuid = uuid; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -0,0 +1,21 @@ | ||||
| package com.baeldung.readonlytransactions.mysql.spring.repositories; | ||||
| 
 | ||||
| import org.springframework.data.jpa.repository.JpaRepository; | ||||
| import org.springframework.data.jpa.repository.Query; | ||||
| 
 | ||||
| import com.baeldung.readonlytransactions.mysql.spring.ReaderDS; | ||||
| import com.baeldung.readonlytransactions.mysql.spring.entities.BookEntity; | ||||
| 
 | ||||
| import javax.transaction.Transactional; | ||||
| 
 | ||||
| public interface BookRepository extends JpaRepository<BookEntity, Long> { | ||||
| 
 | ||||
|     @ReaderDS | ||||
|     @Query("Select 1 from BookEntity t where t.id = ?1") | ||||
|     Long get(Long id); | ||||
| 
 | ||||
|     @Transactional | ||||
|     default BookEntity persist(BookEntity book) { | ||||
|         return this.save(book); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| package com.baeldung.readonlytransactions.utils; | ||||
| 
 | ||||
| import java.util.concurrent.ExecutorService; | ||||
| import java.util.concurrent.LinkedBlockingQueue; | ||||
| import java.util.concurrent.ThreadPoolExecutor; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| 
 | ||||
| public class ExecutorUtils { | ||||
| 
 | ||||
|     private ExecutorUtils() { | ||||
|     } | ||||
| 
 | ||||
|     public static ExecutorService createExecutor(int corePoolSize, int maximumPoolSize) { | ||||
|         return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.DiscardOldestPolicy()); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,23 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|              xmlns="http://xmlns.jcp.org/xml/ns/persistence" | ||||
|              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence | ||||
|              http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" | ||||
|              version="2.1"> | ||||
| 
 | ||||
|     <persistence-unit name="jpa-unit" transaction-type="RESOURCE_LOCAL"> | ||||
|         <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> | ||||
|         <class>com.baeldung.readonlytransactions.mysql.entities.Book</class> | ||||
|         <exclude-unlisted-classes>true</exclude-unlisted-classes> | ||||
|         <properties> | ||||
|             <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/> | ||||
|             <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/baeldung?useUnicode=true&characterEncoding=UTF-8"/> | ||||
|             <property name="javax.persistence.jdbc.user" value="baeldung"/> | ||||
|             <property name="javax.persistence.jdbc.password" value="baeldung"/> | ||||
|             <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/> | ||||
|             <property name="hibernate.hbm2ddl.auto" value="validate"/> | ||||
|             <property name="hibernate.show_sql" value="false"/> | ||||
|             <property name="hibernate.temp.use_jdbc_metadata_defaults" value="true"/> | ||||
|         </properties> | ||||
|     </persistence-unit> | ||||
| </persistence> | ||||
| @ -0,0 +1,18 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <configuration> | ||||
|     <appender name="STDOUT" | ||||
|               class="ch.qos.logback.core.ConsoleAppender"> | ||||
|         <encoder> | ||||
|             <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - | ||||
|                 %msg%n | ||||
|             </pattern> | ||||
|         </encoder> | ||||
|     </appender> | ||||
| 
 | ||||
|     <logger name="org.hibernate.SQL" level="DEBUG"/> | ||||
|     <logger name="org.hibernate.engine.transaction.internal.TransactionImpl" level="DEBUG"/> | ||||
| 
 | ||||
|     <root level="INFO"> | ||||
|         <appender-ref ref="STDOUT"/> | ||||
|     </root> | ||||
| </configuration> | ||||
| @ -0,0 +1,77 @@ | ||||
| package com.baeldung.readonlytransactions; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertNotNull; | ||||
| 
 | ||||
| import org.hibernate.Session; | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.junit.jupiter.api.extension.ExtendWith; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.beans.factory.annotation.Qualifier; | ||||
| import org.springframework.context.ApplicationContextInitializer; | ||||
| import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; | ||||
| import org.springframework.context.support.GenericApplicationContext; | ||||
| import org.springframework.test.context.ContextConfiguration; | ||||
| import org.springframework.test.context.junit.jupiter.SpringExtension; | ||||
| import org.springframework.test.context.support.AnnotationConfigContextLoader; | ||||
| 
 | ||||
| import com.baeldung.readonlytransactions.h2.Config; | ||||
| import com.baeldung.readonlytransactions.h2.Book; | ||||
| import com.baeldung.readonlytransactions.mysql.spring.ReadOnlyInterception; | ||||
| 
 | ||||
| import java.util.UUID; | ||||
| 
 | ||||
| import javax.persistence.EntityManager; | ||||
| import javax.persistence.EntityManagerFactory; | ||||
| 
 | ||||
| @ExtendWith(SpringExtension.class) | ||||
| @ContextConfiguration(loader = AnnotationConfigContextLoader.class, initializers = JPATransactionIntegrationTest.TestConfig.class, classes = { ReadOnlyInterception.class }) | ||||
| class JPATransactionIntegrationTest { | ||||
| 
 | ||||
|     static class TestConfig implements ApplicationContextInitializer<GenericApplicationContext> { | ||||
|         @Override | ||||
|         public void initialize(GenericApplicationContext applicationContext) { | ||||
|             new AnnotatedBeanDefinitionReader(applicationContext).register(Config.class); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Autowired | ||||
|     @Qualifier("h2EntityManagerFactory") | ||||
|     private EntityManagerFactory entityManagerFactory; | ||||
| 
 | ||||
|     @BeforeEach | ||||
|     void setUp() { | ||||
|         EntityManager entityManager = entityManagerFactory.createEntityManager(); | ||||
|         entityManager.getTransaction() | ||||
|             .begin(); | ||||
|         entityManager.createQuery("DELETE FROM Book") | ||||
|             .executeUpdate(); | ||||
| 
 | ||||
|         Book book = new Book(); | ||||
|         book.setName("Test 1"); | ||||
|         book.setUuid(UUID.randomUUID() | ||||
|             .toString()); | ||||
| 
 | ||||
|         entityManager.merge(book); | ||||
|         entityManager.getTransaction() | ||||
|             .commit(); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void givenAEntityManagerDefinedAsReadOnly_whenCreatingATransaction_thenAReadOnlyTransactionShouldBeCreated() { | ||||
|         EntityManager entityManager = entityManagerFactory.createEntityManager(); | ||||
| 
 | ||||
|         entityManager.unwrap(Session.class) | ||||
|             .setDefaultReadOnly(true); | ||||
|         entityManager.getTransaction() | ||||
|             .begin(); | ||||
|         Book book = entityManager.find(Book.class, 1L); | ||||
|         entityManager.getTransaction() | ||||
|             .commit(); | ||||
|         entityManager.unwrap(Session.class) | ||||
|             .setDefaultReadOnly(false); | ||||
| 
 | ||||
|         assertNotNull(book); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,72 @@ | ||||
| package com.baeldung.readonlytransactions; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertNotNull; | ||||
| 
 | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.junit.jupiter.api.extension.ExtendWith; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.beans.factory.annotation.Qualifier; | ||||
| import org.springframework.context.ApplicationContextInitializer; | ||||
| import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; | ||||
| import org.springframework.context.support.GenericApplicationContext; | ||||
| import org.springframework.test.context.ContextConfiguration; | ||||
| import org.springframework.test.context.junit.jupiter.SpringExtension; | ||||
| import org.springframework.test.context.support.AnnotationConfigContextLoader; | ||||
| 
 | ||||
| import com.baeldung.readonlytransactions.h2.Config; | ||||
| import com.baeldung.readonlytransactions.h2.Book; | ||||
| import com.baeldung.readonlytransactions.h2.TransactionConfig; | ||||
| import com.baeldung.readonlytransactions.h2.BookService; | ||||
| 
 | ||||
| import java.util.UUID; | ||||
| 
 | ||||
| import javax.persistence.EntityManager; | ||||
| import javax.persistence.EntityManagerFactory; | ||||
| 
 | ||||
| @ExtendWith(SpringExtension.class) | ||||
| @ContextConfiguration(loader = AnnotationConfigContextLoader.class, initializers = SpringTransactionReadOnlyIntegrationTest.TestConfig.class, classes = { BookService.class }) | ||||
| class SpringTransactionReadOnlyIntegrationTest { | ||||
| 
 | ||||
|     static class TestConfig implements ApplicationContextInitializer<GenericApplicationContext> { | ||||
|         @Override | ||||
|         public void initialize(GenericApplicationContext applicationContext) { | ||||
|             AnnotatedBeanDefinitionReader beanDefinitionReader = new AnnotatedBeanDefinitionReader(applicationContext); | ||||
| 
 | ||||
|             beanDefinitionReader.register(Config.class); | ||||
|             beanDefinitionReader.register(TransactionConfig.class); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Autowired | ||||
|     @Qualifier("h2EntityManagerFactory") | ||||
|     private EntityManagerFactory entityManagerFactory; | ||||
| 
 | ||||
|     @Autowired | ||||
|     private BookService service; | ||||
| 
 | ||||
|     @BeforeEach | ||||
|     void setUp() { | ||||
|         EntityManager entityManager = entityManagerFactory.createEntityManager(); | ||||
|         entityManager.getTransaction() | ||||
|             .begin(); | ||||
|         entityManager.createQuery("DELETE FROM Book") | ||||
|             .executeUpdate(); | ||||
| 
 | ||||
|         Book book = new Book(); | ||||
|         book.setName("Test 1"); | ||||
|         book.setUuid(UUID.randomUUID() | ||||
|             .toString()); | ||||
| 
 | ||||
|         entityManager.merge(book); | ||||
|         entityManager.getTransaction() | ||||
|             .commit(); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void givenThatSpringTransactionManagementIsEnabled_whenAMethodIsAnnotatedAsTransactionalReadOnly_thenSpringShouldTakeCareOfTheTransaction() { | ||||
|         Book book = service.getBookById(1L); | ||||
| 
 | ||||
|         assertNotNull(book); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,106 @@ | ||||
| package com.baeldung.readonlytransactions; | ||||
| 
 | ||||
| import static java.util.stream.Collectors.toList; | ||||
| import static org.junit.jupiter.api.Assertions.assertNotNull; | ||||
| 
 | ||||
| import org.junit.jupiter.api.Disabled; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.junit.jupiter.api.extension.ExtendWith; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.context.ApplicationContextInitializer; | ||||
| import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; | ||||
| import org.springframework.context.support.GenericApplicationContext; | ||||
| import org.springframework.test.context.ContextConfiguration; | ||||
| import org.springframework.test.context.junit.jupiter.SpringExtension; | ||||
| import org.springframework.test.context.support.AnnotationConfigContextLoader; | ||||
| 
 | ||||
| import com.baeldung.readonlytransactions.mysql.dao.MyRepoJPA; | ||||
| import com.baeldung.readonlytransactions.mysql.dao.MyRepoJdbc; | ||||
| import com.baeldung.readonlytransactions.mysql.dao.MyRepoSpring; | ||||
| import com.baeldung.readonlytransactions.mysql.spring.Config; | ||||
| import com.baeldung.readonlytransactions.mysql.spring.ReadOnlyInterception; | ||||
| import com.baeldung.readonlytransactions.mysql.spring.entities.BookEntity; | ||||
| import com.baeldung.readonlytransactions.mysql.spring.repositories.BookRepository; | ||||
| 
 | ||||
| import java.util.LinkedHashMap; | ||||
| import java.util.Map; | ||||
| import java.util.UUID; | ||||
| import java.util.function.Supplier; | ||||
| import java.util.stream.Stream; | ||||
| 
 | ||||
| // Needs to be run with Docker look at the readme file. | ||||
| @Disabled | ||||
| @ExtendWith(SpringExtension.class) | ||||
| @ContextConfiguration(loader = AnnotationConfigContextLoader.class, initializers = TransactionSetupIntegrationTest.TestConfig.class, classes = { ReadOnlyInterception.class }) | ||||
| class TransactionSetupIntegrationTest { | ||||
| 
 | ||||
|     static class TestConfig implements ApplicationContextInitializer<GenericApplicationContext> { | ||||
|         @Override | ||||
|         public void initialize(GenericApplicationContext applicationContext) { | ||||
|             new AnnotatedBeanDefinitionReader(applicationContext).register(Config.class); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(TransactionSetupIntegrationTest.class); | ||||
| 
 | ||||
|     @Autowired | ||||
|     private MyRepoSpring repoSpring; | ||||
| 
 | ||||
|     @Autowired | ||||
|     private BookRepository repository; | ||||
| 
 | ||||
|     @Test | ||||
|     void givenTheDifferentTransactionSetup_whenRunningAThroughputTest_thenWeCanObserveTheSystem() { | ||||
|         Map<String, Supplier<Long>> jdbcConfigurations = new LinkedHashMap<>(); | ||||
| 
 | ||||
|         jdbcConfigurations.put("JPA: Session read only true and autocommit disabled", () -> new MyRepoJPA().runQuery()); | ||||
| 
 | ||||
|         jdbcConfigurations.put("Spring: Session read only and autocommit true", () -> repoSpring.runQuery()); | ||||
| 
 | ||||
|         jdbcConfigurations.put("JDBC: Global read only and autocommit enabled", () -> new MyRepoJdbc(true, true).runQuery(null, null)); | ||||
|         jdbcConfigurations.put("JDBC: Global read only false and autocommit enabled", () -> new MyRepoJdbc(false, true).runQuery(null, null)); | ||||
|         jdbcConfigurations.put("JDBC: Global read only true and autocommit disabled", () -> new MyRepoJdbc(true, false).runQuery(null, null)); | ||||
| 
 | ||||
|         jdbcConfigurations.put("JDBC: Session read only and autocommit disabled", () -> new MyRepoJdbc(false, false).runQuery(false, false)); | ||||
|         jdbcConfigurations.put("JDBC: Session read only and autocommit enabled", () -> new MyRepoJdbc(false, false).runQuery(true, true)); | ||||
|         jdbcConfigurations.put("JDBC: Session read only false and autocommit enabled", () -> new MyRepoJdbc(false, false).runQuery(false, true)); | ||||
|         jdbcConfigurations.put("JDBC: Session read only true and autocommit disabled", () -> new MyRepoJdbc(false, false).runQuery(true, false)); | ||||
| 
 | ||||
|         jdbcConfigurations.entrySet() | ||||
|             .stream() | ||||
|             .flatMap(entry -> { | ||||
|                 Stream.Builder<String> builder = Stream.builder(); | ||||
|                 return builder.add(entry.getKey() + " Total: " + entry.getValue() | ||||
|                         .get()) | ||||
|                     .add(entry.getKey() + " Total: " + entry.getValue() | ||||
|                         .get()) | ||||
|                     .add(entry.getKey() + " Total: " + entry.getValue() | ||||
|                         .get()) | ||||
|                     .build(); | ||||
|             }) | ||||
|             .collect(toList()) | ||||
|             .stream() | ||||
|             .peek(o -> logger.info("--------------------------------------------------")) | ||||
|             .forEach(logger::info); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void givenThatSpringTransactionManagementIsEnabled_whenAMethodIsAnnotatedAsTransactionalReadOnly_thenSpringShouldTakeCareOfTheTransaction() { | ||||
|         Long id = repository.get(2L); | ||||
| 
 | ||||
|         assertNotNull(id); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void givenThatSpringTransactionManagementIsEnabled_whenAMethodIsAnnotatedAsTransactional_thenSpringShouldTakeCareOfTheTransaction() { | ||||
|         BookEntity book = new BookEntity(); | ||||
|         book.setName("Persistence test"); | ||||
|         book.setUuid(UUID.randomUUID() | ||||
|             .toString()); | ||||
|         book = repository.persist(book); | ||||
| 
 | ||||
|         assertNotNull(book.getId()); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user