4 Nisan 2023 Salı

SpringData JPA Multiple Databases Kullanımı - @EnableJpaRepositories İle

Giriş
İzlenmesi gereken adımların sırası şöyle
1. İki tane DataSource yaratılır. Bir tanesi @Primary olarak işaretlenir.
2. İki tane LocalContainerEntityManagerFactoryBean yaratılır. Bir tanesi @Primary olarak işaretlenir. Her birisine ilgili DataSource atanır
3. @EnableJpaRepositories anotasyonunda ilgili LocalContainerEntityManagerFactoryBean ve PlatformTransactionManager belirtilir. basePackages ile hangi sınıfların kullanılacağı belirtilir. Gerekiyorsa excludeFilters ile hangi JPA sınıflarının kullanılmayacağı belirtilir.
Bir örnek burada.

Örnek - Master ve Slave DB Ayrımı
Birinci veri tabanı konfigürasyonu için şöyle yaparız. Burada excludeFilters alanı önemli.
@Configuration
@EnableJpaRepositories(
        basePackages = "...",
        excludeFilters = @ComponentScan.Filter(ReadOnlyRepository.class),
        entityManagerFactoryRef = "primaryEntityManagerFactory"
)
public class PrimaryDataSourceConfiguration {

  @Bean
  @Primary
  public DataSource primaryDataSource() {
    ...
  }

  @Bean
  @Primary
  public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory() {
    ...    
  }
}
İkinci veri tabanı konfigürasyonu için şöyle yaparız. Burada includeFilters alanı önemli.
@Configuration
@EnableJpaRepositories(
        basePackages = "...",
        includeFilters = @ComponentScan.Filter(ReadOnlyRepository.class),
        entityManagerFactoryRef = "readOnlyEntityManagerFactory"
)
public class ReadOnlyDataSourceConfiguration {


  @Bean
  public DataSource readDataSource() {
    ...
  }

  @Bean
  public LocalContainerEntityManagerFactoryBean readOnlyEntityManagerFactory() {
    ...
  }
}
Seçim için kullanılacak anotasyon şöyledir
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ReadOnlyRepository {
}
Entity ve Repository sınıfları şöyledir
@Entity
@Table(name = "books")
@Data
public class Books {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  ...
}

@Repository
@ReadOnlyRepository
public interface BooksReadOnlyRepository extends JpaRepository<Books, Long> {
}

@Repository
public interface BooksReadWriteRepository extends JpaRepository<Books, Long> {
}
Bunları kullanmak için şöyle yaparız
@Repository
public class BooksDAO implements BooksReadOnlyRepository, BooksReadWriteRepository {
  private BooksReadOnlyRepository booksReadOnlyRepository;
  private BooksReadWriteRepository booksReadWriteRepository;

  @Autowired
  public BooksDAO(BooksReadOnlyRepository booksReadOnlyRepository,
BooksReadWriteRepository booksReadWriteRepository) {
    this.booksReadOnlyRepository = booksReadOnlyRepository;
    this.booksReadWriteRepository = booksReadWriteRepository;
  }

  public List<Books> getAllBooksFromMaster() {
    return booksReadWriteRepository.findAll();
  }

  public List<Books> getAllBooksFromSlave() {
    return booksReadOnlyRepository.findAll();
  }
  ...
}
Örnek
application.properties şöyle olsun. Burada bir bağlantı normal Spring kuralları ile yapılıyor. Test için olan JPA ise elle kodlanıyor
#DataSource
spring.test1.jdbc-url=jdbc:mysql://localhost:3306/test1
spring.test1.username=root
spring.test1.password=root@1234
spring.test1.driverClassName=com.mysql.cj.jdbc.Driver

spring.test2.jdbc-url=jdbc:mysql://localhost:3306/test2
spring.test2.username=root
spring.test2.password=root@1234
spring.test2.driverClassName=com.mysql.cj.jdbc.Driver

#JPA
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.properties.hibernate.format_sql=true
Test bağlantısı için şöyle yaparız
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "personEntityManagerfactoryBean",
	transactionManagerRef = "personTransactionManager",
	basePackages = {"com.spring.boot.multipledatabase.repo.person"}
)
public class Test2DbConfig {
	
  @Bean(name = "test2DataSource")
  @ConfigurationProperties(prefix = "spring.test2")
  public DataSource test1Datasource() {
    return DataSourceBuilder.create().build();
  }
	
  @Bean(name = "personEntityManagerfactoryBean")
  public LocalContainerEntityManagerFactoryBean entityManagerfactoryBean(
    EntityManagerFactoryBuilder builder,
    @Qualifier("test2DataSource") DataSource dataSource) {
      return builder.dataSource(dataSource)
        .packages("com.spring.boot.multipledatabase.entity.person")
	.persistenceUnit("PERSON")
	.build();
  }
  @Bean(name = "personTransactionManager")
  public PlatformTransactionManager transactionManager(
    @Qualifier("personEntityManagerfactoryBean") 
    EntityManagerFactory entityManagerFactory) {
    return new JpaTransactionManager(entityManagerFactory);
  }
}
Örnek
application.properties şöyle olsun. Kodla iki tane JPA bağlantısı açacağız
#Primary database connection
spring.primary.datasource.url = jdbc:postgresql://localhost:5432/MultipleDbDemo
spring.primary.datasource.username = postgres
spring.primary.datasource.password = 1234

#Secondary database connection
spring.secondary.datasource.url = jdbc:sqlserver://localhost:1433;databaseName=MultipleDbDemoSqlServer;encrypt=true;trustServerCertificate=true
spring.secondary.datasource.username = sa
spring.secondary.datasource.password = 1234
	 #spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.SQLServerDialect

spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.show-sql=true
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation= true
spring.jpa.properties.javax.persistence.validation.mode = none


server.port:3000
Birinci bağlantı için şöyle yaparız. Burada önemli olan JPA modellerini farklı bir pakette toplamak
@Configuration
@EnableJpaRepositories(entityManagerFactoryRef = "primaryEntityManagerFactory", 
		transactionManagerRef = "primaryTransactionManager", 
		basePackages = {"merveozer.multipledb.primary.repository"})
public class PrimaryDatabaseConnection {

  @Value("${spring.primary.datasource.url}")
  private String url;
	
  @Value("${spring.primary.datasource.username}")
  private String username;
	
  @Value("${spring.primary.datasource.password}")
  private String password;
	
  @Primary
  @Bean(name="primaryDbDataSource")
  public DataSource primaryDbDataSource() {
    return DataSourceBuilder.create()
      .url(url).username(username).password(password).build();
  }
	
  @Primary
  @Bean(name = "primaryEntityManagerFactory")
  public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
    EntityManagerFactoryBuilder builder,
    @Qualifier("primaryDbDataSource") DataSource primaryDataSource) {
    return builder.dataSource(primaryDataSource)
      .packages("merveozer.multipledb.primary.model")
      .build();
  }

  @Primary
  @Bean(name = "primaryTransactionManager")
  public PlatformTransactionManager primaryTransactionManager(
    @Qualifier("primaryEntityManagerFactory") EntityManagerFactory
    primaryEntityManagerFactory) {
      return new JpaTransactionManager(primaryEntityManagerFactory);
    }
}
İkinci bağlantı için de şöyle yaparız. JPA modelleri yine farklı bir pakette.
@Configuration
@EnableJpaRepositories(entityManagerFactoryRef = "secondaryEntityManagerFactory", 
  transactionManagerRef = "secondaryTransactionManager", 
  basePackages = {"merveozer.multipledb.secondary.repository"})
public class SecondaryDatabaseConnection {
  @Value("${spring.secondary.datasource.url}")
  private String url;
	
  @Value("${spring.secondary.datasource.username}")
  private String username;
	
  @Value("${spring.secondary.datasource.password}")
  private String password;

  @Bean(name="secondaryDbDataSource")
  public DataSource secondaryDbDataSource() {
    return DataSourceBuilder.create().url(url)
      .username(username).password(password).build();
  }
	
  @Bean(name = "secondaryEntityManagerFactory")
  public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
    @Qualifier("secondaryDbDataSource") DataSource secondaryDataSource, 
    EntityManagerFactoryBuilder builder) {
    return builder.dataSource(secondaryDataSource)
      .packages("merveozer.multipledb.secondary.model").build();
  }

  @Bean(name = "secondaryTransactionManager")
  public PlatformTransactionManager secondaryTransactionManager(
    @Qualifier("secondaryEntityManagerFactory") EntityManagerFactory
      secondaryEntityManagerFactory) {
      return new JpaTransactionManager(secondaryEntityManagerFactory);
  }
}




Hiç yorum yok:

Yorum Gönder