I have been trying to configure openjpa-slice with spring boot. The configuration is shared as follows:
#Configuration
public class DatasourceConfiguration extends JpaBaseConfiguration{
#Bean(name = "slice1")
public DataSource dataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource dataSource = dataSourceLookup.getDataSource("java:/MysqlXADS");
return dataSource;
}
#Bean(name = "slice2")
public DataSource dataSource2() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource dataSource = dataSourceLookup.getDataSource("java:/MysqlXADS2");
return dataSource;
}
#Override
protected Map<String, Object> getVendorProperties() {
HashMap<String, Object> map = new HashMap<String, Object>();
return map;
}
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("openjpa.BrokerFactory", "slice");
properties.put("openjpa.slice.Names", "One, Two");
properties.put("openjpa.slice.Master", "Two");
properties.put("openjpa.slice.Lenient", "false");
properties.put("openjpa.slice.ConnectionDriverName", "com.mysql.jdbc.Driver");
properties.put("openjpa.slice.One.ConnectionFactoryName", "java:/MysqlXADS");
properties.put("openjpa.slice.Two.ConnectionFactoryName", "java:/MysqlXADS2");
properties.put("openjpa.slice.DistributionPolicy", "com.services.sample.configuration.UserDistributionPolicy");
properties.put("openjpa.slice.TransactionPolicy" , "xa");
properties.put("openjpa.TransactionMode" , "managed");
properties.put("openjpa.ConnectionFactoryMode", "managed");
properties.put("openjpa.jdbc.DBDictionary", "mysql");
properties.put("openjpa.Log","DefaultLevel=WARN, Runtime=INFO, Tool=INFO, SQL=TRACE");
return builder
.dataSource(dataSource()).jta(true)
.properties(properties)
.packages("com.services.sample.domain.entity")
.persistenceUnit("sample")
.build();
}
#Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
OpenJpaVendorAdapter jpaVendorAdapter = new OpenJpaVendorAdapter();
jpaVendorAdapter.setShowSql(true);
return jpaVendorAdapter;
}
public PlatformTransactionManager transactionManager(#Qualifier("entityManagerFactory")EntityManagerFactory entityManagerFactory){
JtaTransactionManager transactionManager = new JtaTransactionManager();
return transactionManager;
}
}
persistence.xml
<persistence-unit name="sample" transaction-type="JTA" >
<description>generated-persistence-unit</description>
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<class> ... </class>
</persistence-unit>
I have configured this with wildfly server 10. Both the xa-datasources point to different databases. However, when the application boots and I execute a query it just fetches the data from the datasource passed into the builder twice. Seems I have something wrong with the slice configuration. Not sure whats the problem in this case.
Related
I have implemented the application using Spring RoutingDataSource.
Spring -> DS1,
DS2
Based on the logged in URL I am changing the Data Source. it is working fine.
Coming to the quartz, I am unable to change the data source dynamically. Always jobs are getting scheduled on default data source.
#Configuration
public class SchedulerConfig {
#Autowired
private DataSource dataSource;
#Autowired
private QuartzProperties quartzProperties;
#Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
#Bean
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) {
Properties properties = new Properties();
properties.putAll(quartzProperties.getProperties());
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setJobFactory(jobFactory);
factory.setDataSource(dataSource);
factory.setGlobalJobListeners(jobListener());
factory.setQuartzProperties(properties);
return factory;
}
#Bean
public JobListenerSupport jobListener() {
return new JobListener();
}
}
Data Source Routing Configuration::
#Component
public class DataSourceRouting extends AbstractRoutingDataSource {
private DataSourceOneConfig dataSourceOneConfig;
private DataSourceTwoConfig dataSourceTwoConfig;
private DataSourceContextHolder dataSourceContextHolder;
public DataSourceRouting(DataSourceContextHolder dataSourceContextHolder, DataSourceOneConfig dataSourceOneConfig,
DataSourceTwoConfig dataSourceTwoConfig) {
this.dataSourceOneConfig = dataSourceOneConfig;
this.dataSourceTwoConfig = dataSourceTwoConfig;
this.dataSourceContextHolder = dataSourceContextHolder;
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put(DataSourceEnum.tenant1, dataSourceOneDataSource());
dataSourceMap.put(DataSourceEnum.tenant2, dataSourceTwoDataSource());
this.setTargetDataSources(dataSourceMap);
this.setDefaultTargetDataSource(dataSourceTwoDataSource());
}
#Override
protected Object determineCurrentLookupKey() {
return dataSourceContextHolder.getBranchContext();
}
public DataSource dataSourceOneDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(dataSourceOneConfig.getUrl());
dataSource.setUsername(dataSourceOneConfig.getUsername());
dataSource.setPassword(dataSourceOneConfig.getPassword());
return dataSource;
}
public DataSource dataSourceTwoDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(dataSourceTwoConfig.getUrl());
dataSource.setUsername(dataSourceTwoConfig.getUsername());
dataSource.setPassword(dataSourceTwoConfig.getPassword());
return dataSource;
}
}
Data Soruce Config ::
#RequiredArgsConstructor
#DependsOn("dataSourceRouting")
public class DataSourceConfig {
private final DataSourceRouting dataSourceRouting;
#Bean
#Primary
public DataSource dataSource() {
return dataSourceRouting;
}
#Bean(name = "entityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
return builder.dataSource(dataSource()).packages("com.model.entity").build();
}
#Bean(name = "transcationManager")
public JpaTransactionManager transactionManager(
#Autowired #Qualifier("entityManager") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
return new JpaTransactionManager(entityManagerFactoryBean.getObject());
}
}
If I have a programmatically managed LocalContainerEntityManagerFactoryBean, like with a #Bean-annotated method:
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "myEntityManager",
transactionManagerRef = "myTransactionManager",
basePackages = {"com.mycompany.my.repository"}
)
public class MyDbConfig {
#Bean
public LocalContainerEntityManagerFactoryBean mcEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
//some production config here
return em;
}
}
How do I add this little JPA property only to be used for integration testing, ideally via something like a listener below (in the test classpath)
public class AfterAllBeansCreatedListener {
#Autowired
private LocalContainerEntityManagerFactoryBean em
#PostConstruct
private void reconfigure() {
HashMap<String, Object> properties = em.getJpaPropertyMap()
properties.put("hibernate.hbm2ddl.auto", "create");
em.setJpaPropertyMap(properties);
}
}
Currently, I am using another #Bean-annotated method producing LocalContainerEntityManagerFactoryBean in a nested #TestConfiguration, but this means that I need to duplicate all the other creation logic for that bean, which is of course pure evil.
The quick and simple solution is to create a Map of properties within the mcEntityManager() method like so
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "myEntityManager",
transactionManagerRef = "myTransactionManager",
basePackages = {"com.mycompany.my.repository"}
)
public class MyDbConfig {
#Bean
public LocalContainerEntityManagerFactoryBean mcEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
HashMap<String, Object> properties = em.getJpaPropertyMap()
properties.put("hibernate.hbm2ddl.auto", "create");
em.setJpaPropertyMap(properties);
return em;
}
}
If you need different JpaProperty map values per env, you'd move the property map creatation to it's own Profile specific methods, like
#Bean
#Profile("dev)
public Map<String,Object> getMyJpaPropertyMap() {
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "create");
properties.put("hibernate.temp.use_jdbc_metadata_defaults",false);
properties.put("hibernate.jdbc.lob.non_contextual_creation",true);
}
#Bean
#Profile("prod)
public Map<String,Object> getMyJpaPropertyMap() {
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.temp.use_jdbc_metadata_defaults",true);
}
and the mcEntityManager() method is updated to
#Bean
public LocalContainerEntityManagerFactoryBean mcEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setJpaPropertyMap(getMyJpaPropertyMap());
return em;
}
I am running the latest version of spring boot app with spring Kafka and MySQL as database and used KafkaChainedTransactionManager for transaction synchronization.
I want to update the same entity object by a particular listener. so when the multiple messages are coming to the topic at a time for updating the same object, the transaction is not waiting until the transaction commits for the same object. so the data in becoming inconsistent.
I have tried using Pessimistic lock-in JPA repository query with no luck.
Listener where the data inconsistency happening when the multiple messages are receiving at the same for updating the same object
// method
#Autowired
private ProcessIndexRepository processIndexRepository;
#Transactional(readonly=false)
#KafkaListener(id = "update_process", topics = "update_process")
public update(#Payload String message){
ProcessModel processModel= JsonUtil.toObject(message,ProcessModel.class);
// from repository it will get old data instead of updated data
ProcessIndex process= processIndexRepository.findByProcessId(processModel.getId()).get();
if(processModel.getName()!=null){
process.set(processModel.getName())
}
if(processModel.getAge()>0){
processModel.setAge(processModel.getAge())
}
processIndexRepository.save(process);
}
Kafka sender config
#Configuration
#EnableKafka
public class KafkaSenderConfig{
#Value("${kafka.servers}")
private String kafkaServers;
#Value("${application.name}")
private String applicationName;
#Bean(value = "stringKafkaTransactionManager")
public KafkaTransactionManager<String, String> kafkaStringTransactionManager() {
KafkaTransactionManager<String, String> ktm = new KafkaTransactionManager<String, String>(stringProducerFactory());
ktm.setNestedTransactionAllowed(true);
ktm.setTransactionSynchronization(AbstractPlatformTransactionManager.SYNCHRONIZATION_ALWAYS);
return ktm;
}
#Bean(value = "stringProducerFactory")
#Primary
public ProducerFactory<String, String> stringProducerFactory() {
Map<String, Object> config = new ConcurrentHashMap<>();
config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaServers);
config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
config.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
config.put(ProducerConfig.LINGER_MS_CONFIG, 100);
config.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);
config.put(ProducerConfig.ACKS_CONFIG, "all");
DefaultKafkaProducerFactory<String, String> defaultKafkaProducerFactory = new DefaultKafkaProducerFactory<>(config);
defaultKafkaProducerFactory.setTransactionIdPrefix("sample-trans-");
return defaultKafkaProducerFactory;
}
#Bean(value = "stringKafkaTemplate")
#Primary
public KafkaTemplate<String, String> stringKafkaTemplate() {
return new KafkaTemplate<>(stringProducerFactory(),true);
}
#Bean(name = "chainedStringKafkaTransactionManager")
#Primary
public ChainedKafkaTransactionManager<String, String> chainedTransactionManager(JpaTransactionManager jpaTransactionManager, DataSourceTransactionManager dsTransactionManager) {
return new ChainedKafkaTransactionManager<>(kafkaStringTransactionManager(), jpaTransactionManager, dsTransactionManager);
}
}
Kafka receiver config
#Configuration
#EnableKafka
public class KafkaReceiverConfig {
#Value("${kafka.servers}")
private String kafkaServers;
#Value("${kafka.groupId}")
private String groupId;
#Value("${kafka.retry.maxAttempts}")
private Integer retryMaxAttempts;
#Value("${kafka.retry.interval}")
private Long retryInterval;
#Value("${kafka.concurrency}")
private Integer concurrency;
#Value("${kafka.poll.timeout}")
private Integer pollTimeout;
#Value("${kafka.consumer.auto-offset-reset:earliest}")
private String offset = "earliest";
#Autowired
private PlatformTransactionManager transactionManager;
#Bean
public RetryPolicy retryPolicy() {
SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
simpleRetryPolicy.setMaxAttempts(retryMaxAttempts);
return simpleRetryPolicy;
}
#Bean
public BackOffPolicy backOffPolicy() {
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(retryInterval);
return backOffPolicy;
}
#Bean
public RetryTemplate retryTemplate(){
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(retryPolicy());
retryTemplate.setBackOffPolicy(backOffPolicy());
return retryTemplate;
}
#Bean
public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<String, String>();
factory.setConsumerFactory(consumerFactory());
factory.setConcurrency(concurrency);
factory.getContainerProperties().setPollTimeout(pollTimeout);
factory.getContainerProperties().setSyncCommits(true);
factory.setRetryTemplate(retryTemplate());
factory.getContainerProperties().setAckOnError(false);
factory.getContainerProperties().setTransactionManager(transactionManager);
return factory;
}
#Bean
public ConsumerFactory<String, String> consumerFactory() {
return new DefaultKafkaConsumerFactory<>(consumerConfigs());
}
#Bean
public Map<String, Object> consumerConfigs() {
Map<String, Object> props = new ConcurrentHashMap<String, Object>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaServers);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, offset);
props.put(ConsumerConfig.ISOLATION_LEVEL_CONFIG, "read_committed");
return props;
}
#Bean(name = { "jsonConsumerFactory" })
public ConsumerFactory<String, Object> jsonConsumerFactory() {
Map<String, Object> props = new ConcurrentHashMap<>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaServers);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonSerializer.class);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
return new DefaultKafkaConsumerFactory<>(props);
}
#Bean(name = { "kafkaJsonListenerContainerFactory" })
public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, Object>> kafkaJsonListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, Object> factory = new ConcurrentKafkaListenerContainerFactory<String, Object>();
factory.setConsumerFactory(jsonConsumerFactory());
factory.setConcurrency(concurrency);
factory.getContainerProperties().setPollTimeout(pollTimeout);
factory.getContainerProperties().setSyncCommits(true);
return factory;
}
data source config
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "com.sample.entity.repository")
public class DatasourceConfig {
#Bean(name = "dataSourceProperties")
#ConfigurationProperties("spring.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name = "datasource")
#Primary
public DataSource dataSource(#Qualifier("dataSourceProperties") DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().type(HikariDataSource.class)
.build();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(#Qualifier("datasource") DataSource ds) throws PropertyVetoException {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(ds);
entityManagerFactory.setPackagesToScan(new String[]{"com.sample.entity.domain"});
JpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter);
return entityManagerFactory;
}
#Bean
public DataSourceTransactionManager dsTransactionManager(#Qualifier("datasource") DataSource ds) {
return new DataSourceTransactionManager(ds);
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory){
return jpaTransactionManager(entityManagerFactory);
}
#Bean
public JpaTransactionManager jpaTransactionManager(EntityManagerFactory entityManagerFactory){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
#Bean
public JdbcTemplate jdbcTemplate(#Qualifier("datasource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
property file(Kafka config)
kafka.servers=localhost:9092
kafka.groupId=xyzabc
kafka.retry.maxAttempts=3
kafka.retry.interval=300000
kafka.concurrency=10
kafka.poll.timeout=1000
When updating the same object it should wait until the transaction commits then only the JPA repository should pick the updated object and update newer things
Your main culprit is threading below line.
kafka.concurrency=10
When multiple threads are beginning and committing the transaction for same object you should synchronize that code block else you may not be able to maintain the consistency. It will happen for any code where simultaneous updates are happing for same records.
You can avoid race conditions by using the partition key.
I have two spring transaction managers.
txManager1 - ChainedKafkaTransactionManager (KafkaTransactionManager,JpaTransactionManager) configured with datasource DB1
txManager2 - JpaTransactionManager configured with datasource DB2
The problem is that I perform some operation using txManager2 but somehow txManager1 is being used instead of txManager2 and the data is getting committed to DB1 instead of DB2.
#Autowired
#Qualifier("common-tx")
private PlatformTransactionManager txManager2 ;
#KafkaListener(topics = "${kafka.topic.name}", groupId = "group-1", containerFactory = "customKafkaListenerContainerFactory")
public void topicListener(String message, Acknowledgment ack)
throws InterruptedException, ClassNotFoundException, IOException {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName(domainEvent.getEventId());
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus commonStatus = txManager2 .getTransaction(def);
someService.doSomething();
txManager2 .commit(commonStatus);
ack.acknowledge();
}
In doSomething() I am just persisting an entity, upon debugging it is found that while saving the entity via Spring data repository, the transaction manager
determined in invokeWithinTransaction() of org.springframework.transaction.interceptor.TransactionAspectSupport is wrong, i.e. txManager1 is selected instead of txManager2, is it a configuration issue or am I missing something?
txManager1 configuration :
#Configuration
#EnableTransactionManagement
#PropertySource(value = {"classpath:application-${spring.profiles.active}.properties"})
#Profile({"development","production","qa"})
#EnableJpaRepositories(basePackages={"xxx.xxx.xxxx"},excludeFilters=#ComponentScan.Filter(type=FilterType.REGEX, pattern="xxx.xxx.xxxx.module2.*"))
public class JPAConfig1 {
#Value("${jndi.name}")
private String jndiName;
#Value("${hibernate.dialect}")
private String hibernateDialect;
#Value("${hibernate.show_sql}")
private String showSql;
#Value("${hibernate.format_sql}")
private String formatSql;
#Value("${hibernate.hbm2ddl.auto}")
private String hiberanteUpdate;
#Value("${javax.persistence.validation.mode}")
private String hibernateValidation;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactory(MultiTenantConnectionProviderImpl tenantConnection, CurrentTenantIdentifierResolver currentTenantIdentifierResolver)
{
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
emf.setJpaVendorAdapter(vendorAdapter);
emf.setJpaProperties(jpaProperties(tenantConnection, currentTenantIdentifierResolver));
emf.setPackagesToScan(new String[] {"xxx.xxx.xxxx"});
return emf;
}
private Properties jpaProperties(MultiTenantConnectionProviderImpl tenantConnection, CurrentTenantIdentifierResolver currentTenantIdentifierResolver) {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect",hibernateDialect);
properties.setProperty("hibernate.show_sql",showSql);
properties.setProperty("hibernate.format_sql",formatSql);
properties.setProperty("hibernate.hbm2ddl.auto",hiberanteUpdate);
properties.setProperty("javax.persistence.validation.mode",hibernateValidation);
properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver);
properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, tenantConnection);
return properties;
}
#Bean
public CurrentTenantIdentifierResolver getCurrentTenantIdentifierResolver(TenantContext tenantContext) {
return new CurrentTenantIdentifierResolverImpl(tenantContext);
}
#Bean(name="tenantConnection")
public MultiTenantConnectionProviderImpl getMultiTenantConnectionProvider(TenantContext tenantContext) {
return new MultiTenantConnectionProviderImpl(false,tenantContext);
}
#Bean
#Primary
public PlatformTransactionManager transactionManager(EntityManagerFactory factory,ProducerFactory producerFactory){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(factory);
KafkaTransactionManager tm = new KafkaTransactionManager(producerFactory);
return new ChainedKafkaTransactionManager(tm,transactionManager);
}
#Bean
public TenantContext getTenantContext() {
return new TenantContextImpl();
}
}
txManager2 configuration :
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages="xxx.xxx.xxxx.module2",
entityManagerFactoryRef="common-factory",transactionManagerRef="common-tx")
#PropertySource(value = {"classpath:common-${spring.profiles.active}.properties"})
#Profile({"development","production","qa"})
public class JPAConfig2 {
#Value("${common.jndi.name}")
private String jndiName;
#Value("${common.hibernate.dialect}")
private String hibernateDialect;
#Value("${common.hibernate.show_sql}")
private String showSql;
#Value("${common.hibernate.format_sql}")
private String formatSql;
#Value("${common.hibernate.hbm2ddl.auto}")
private String hiberanteUpdate;
#Value("${common.javax.persistence.validation.mode}")
private String hibernateValidation;
#Bean(name="common-factory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(#Qualifier("common-ds") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan(new String[] {"xxx.xxx.xxxx.module2"});
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(jpaProperties());
em.setPersistenceUnitName("common");
return em;
}
#Bean("common-ds")
public DataSource dataSource() throws NamingException {
return (DataSource) new JndiTemplate().lookup(jndiName);
}
private Properties jpaProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect",hibernateDialect);
properties.setProperty("hibernate.show_sql",showSql);
properties.setProperty("hibernate.format_sql",formatSql);
properties.setProperty("hibernate.hbm2ddl.auto",hiberanteUpdate);
properties.setProperty("javax.persistence.validation.mode",hibernateValidation);
return properties;
}
#Bean(name="common-tx")
public PlatformTransactionManager transactionManager(#Qualifier("common-factory") EntityManagerFactory factory){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(factory);
return transactionManager;
}
}
I use Spring Data JPA and use multiple database, so I must configure it myself instead of using #HibernateJpaAutoConfiguration.
public class TesterDbConfig {
#Autowired(required = false)
private PersistenceUnitManager persistenceUnitManager;
#Bean
public JpaProperties testerJpaProperties() {
JpaProperties jpaProperties = new JpaProperties();
return jpaProperties;
}
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource.primary")
public DataSource testerDataSource() {
return (DataSource) DataSourceBuilder.create().type(DataSource.class).build();
}
#Bean
public LocalContainerEntityManagerFactoryBean testerEntityManager(
JpaProperties testerJpaProperties) {
EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(testerJpaProperties);
return builder.dataSource(testerDataSource()).packages(Cabang.class).persistenceUnit("primary")
.build();
}
#Bean
public JpaTransactionManager testerTransactionManager(EntityManagerFactory testerEntityManager) {
return new JpaTransactionManager(testerEntityManager);
}
private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(
JpaProperties testerJpaProperties) {
JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(testerJpaProperties);
return new EntityManagerFactoryBuilder(jpaVendorAdapter, testerJpaProperties,
this.persistenceUnitManager);
}
private JpaVendorAdapter createJpaVendorAdapter(JpaProperties testerJpaProperties) {
AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(testerJpaProperties.isShowSql());
adapter.setDatabase(testerJpaProperties.getDatabase());
adapter.setDatabasePlatform(testerJpaProperties.getDatabasePlatform());
adapter.setGenerateDdl(testerJpaProperties.isGenerateDdl());
return adapter;
}
}
With this, every time I create a model, I must use #Column in every variable because in my database, I use snake case, but in model class, I use camel case.
How can I set the Hibernate naming strategy?
You can set it via JpaProperties, which you already use in your configuration:
#Bean
public JpaProperties testerJpaProperties() {
JpaProperties jpaProperties = new JpaProperties();
JpaProperties.Hibernate hibernate = new JpaProperties.Hibernate();
hibernate.setNamingStrategy(SpringNamingStrategy.class);
jpaProperties.setHibernate(hibernate);
return jpaProperties;
}
And add
#Bean
public LocalContainerEntityManagerFactoryBean testerEntityManager(
JpaProperties testerJpaProperties) {
EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(testerJpaProperties);
return builder.dataSource(testerDataSource())
.packages(Cabang.class)
.persistenceUnit("primary")
.properties(Collections.singletonMap("hibernate.ejb.naming_strategy",testerJpaProperties.getHibernate().getNamingStrategy()))
.build();
}