I am using AbstractRoutingDataSource so as to have multiple data base connections but, even after changing the contextHoler the database remains same
in a single API call it works perfectly fine but if i change the datasource within the same api call it doesn't change the datasource. if i have 2 api calls with different contextHolder it would work
#GetMapping("/getdata2")
public ClaimAmountAndTCD getDetails2() {
ClaimAmountAndTCD claimAmountAndTCD2 = serviceCls.getBook2Data();
System.out.println(claimAmountAndTCD2);
return claimAmountAndTCD2;
}
#GetMapping("/getdata")
public ClaimAmountAndTCD getDetails() {
ClaimAmountAndTCD claimAmountAndTCD = serviceCls.getBook1Data();
// ClaimAmountAndTCD claimAmountAndTCD2 = serviceCls.getBook2Data();
System.out.println(claimAmountAndTCD);
return claimAmountAndTCD;
}
below is my database config code
#Configuration
#Profile("dev")
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "as400EntityManager", transactionManagerRef = "as400TransactionManager", basePackages = {
"com.spring.demo.repository" })
public class DevDataSourceConfig {
#Value("${db.gmrp.sxcd1.servername}")
private String servername;
#Value("${db.gmrp.sxcd1.username}")
private String username;
#Value("${db.gmrp.sxcd1.password}")
private String password;
#Value("${db.gmrp.sxcd1.libraries}")
private String libraries;
#Value("${db.naming}")
private String dbNaming;
#Value("${db.driver}")
private String driver;
#Value("${db.isolation}")
private String dbIsolation;
#Bean
public AS400JDBCDataSource dataSourceDevSXCD1() {
AS400JDBCDataSource as400DataSource = new AS400JDBCDataSource();
System.out.println(driver + " " + libraries + " " + username + " " + password);
as400DataSource.setDriver(driver);
as400DataSource.setLibraries(libraries);
as400DataSource.setServerName(servername);
as400DataSource.setNaming(dbNaming);
as400DataSource.setTransactionIsolation(dbIsolation);
if (password != null && !password.isEmpty() && password.trim().length() > 0) {
as400DataSource.setUser(username);
as400DataSource.setPassword(password);
}
return as400DataSource;
}
#Bean(name = "as400dev")
#Primary
public DataSource dataSource() {
AbstractRoutingDataSource dataSource = new RoutingDataSource();
Map<Object, Object> resolvedDataSources = new HashMap<>();
resolvedDataSources.put(DbType.DEVSXCD1, dataSourceDevSXCD1());
dataSource.setDefaultTargetDataSource(dataSourceDevSXCD1()); // default
dataSource.setTargetDataSources(resolvedDataSources);
return dataSource;
}
#Bean(name = "as400EntityManager")
#Primary
public LocalContainerEntityManagerFactoryBean as400DevEntityManager(EntityManagerFactoryBuilder builder,
#Qualifier("as400dev") DataSource as400DataSource) {
return builder.dataSource(as400DataSource).packages("com.spring.demo.entity").build();
}
#Bean(name = "as400TransactionManager")
public PlatformTransactionManager as400DevTransactionManager(
#Qualifier("as400EntityManager") EntityManagerFactory entityManager) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManager);
return transactionManager;
}
}
DbContextHoler class
package com.spring.demo.config;
public final class DbContextHolder {
private DbContextHolder() {
// default constructor
}
private static final ThreadLocal<DbType> contextHolder = new ThreadLocal<>();
// set the data source
public static void setDbType(DbType dbType) {
if (dbType == null) {
throw new NullPointerException();
}
contextHolder.set(dbType);
}
// get the current data source in use
public static DbType getDbType() {
return contextHolder.get();
}
// clear data source
public static void clearDbType() {
contextHolder.remove();
}
}
AbstractRoutingDataSource
package com.spring.demo.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class RoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
}
Related
I am trying to use spring cache abstraction with Redis cache. I am unable to see the values in cache. Please help me if I am missing something in config :
As I am making the call multiple times actual fetch is happening. I tried connecting to same redis host port, I cant find there any keys as well.
PFB the implementation details.
CacheUtils.java :
#Slf4j
public class CacheUtils {
private final CustomerManagementClient customerManagementClient;
#Autowired
public CacheUtils(CustomerManagementClient customerManagementClient) {
this.customerManagementClient = customerManagementClient;
}
#Cacheable(value = "merchant-details", key = "#merchantEntityId")
public MerchantDetails getOrFetchMerchantDetails(OrderItemStatusChangeEvent event, MerchantType merchantType, String merchantEntityId) {
if (BUYER == merchantType) {
log.info("test - get buyer details");
CustomerDetails customerDetails =
customerManagementClient.getData(merchantEntityId);
String businessId = customerDetails.getBusinessId();
String phoneNumber = customerDetails.getPhoneNumber();
return MerchantDetails
.builder()
.merchantEntityId(merchantEntityId)
.businessId(businessId)
.businessName(customerDetails.getBusinessName())
.merchantType(merchantType)
.contactNumber(phoneNumber)
.build();
}
throw new InvalidInputException();
}
}
MainClass.java
#Slf4j
#Component
public class MainClass implements LogisticsPlanningService {
private final CacheUtils cacheUtils;
#Autowired
public LogisticsPlanningServiceImpl(CacheUtils cacheUtils) {
this.cacheUtils = cacheUtils;
}
private Set<LogisticsPlanningRequest> testMethod(Event event) {
MerchantDetails senderDetails = cacheUtils.getOrFetchMerchantDetails(event, SELLER, orderItem.getSellerId());
MerchantDetails receiverDetails = cacheUtils.getOrFetchMerchantDetails(event, BUYER, orderItem.getBuyerId());
}
}
RedisConfiguration.java
#Configuration
#EnableCaching
public class RedisConfiguration {
private String hostName;
private int port;
#Autowired
MarketPlaceServiceProperties properties;
#PostConstruct
public void init() {
hostName = properties.getRedisHostName();
port = Integer.parseInt(properties.getRedisPort());
}
#Bean
protected JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(hostName, port);
JedisConnectionFactory factory = new JedisConnectionFactory(configuration);
factory.afterPropertiesSet();
return factory;
}
public RedisCacheConfiguration getTestCacheConfig() {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
cacheConfiguration.prefixCacheNameWith("marketplace");
cacheConfiguration.disableCachingNullValues();
return cacheConfiguration;
}
// #Bean
// public RedisTemplate<String, Object> redisTemplate() {
// final RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
// redisTemplate.setKeySerializer(new StringRedisSerializer());
// redisTemplate.setHashKeySerializer(new GenericToStringSerializer<>(Object.class));
// redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
// redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
// redisTemplate.setConnectionFactory(jedisConnectionFactory());
// return redisTemplate;
// }
}
service.properties :
redisHostName: redis.domain.prod.xyz.com
redisPort: 5400
I have a multi database application. Users can select the database on the login page.
Then the database is routing selected database thanks for AbstractRoutingDataSource from Spring.
I want to use HikariCP, but it needs dataSourceUrl. But my Datasource URL changes dynamically. How can I configure Hikaricp for multiple databases?
File application.properties:
#database1 properties
app.database1.connection.url = url1
app.database1.connection.username = sameusername
app.database1.connection.password = samepassword
#database2 properties
app.database2.connection.url = url2
app.database2.connection.username = sameusername
app.database2.connection.password = samepassword
My Datasource configuration class example:
public class DataSourceConfiguration {
#Autowired(required = false)
private PersistenceUnitManager persistenceUnitManager;
#Bean
#ConfigurationProperties(prefix = "app.database1.connection")
public DataSource database1DataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix = "app.database2.connection")
public DataSource database2DataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#Primary
public DataSource appDataSource() {
DataSourceRouter router = new DataSourceRouter();
final HashMap<Object, Object> map = new HashMap<>(3);
map.put(DatabaseEnvironment.DATABASE1, database1DataSource());
map.put(DatabaseEnvironment.DATABASE2, database2DataSource());
router.setTargetDataSources(map);
return router;
}
#Bean
#Primary
#ConfigurationProperties("app.connection.jpa")
public JpaProperties appJpaProperties() {
return new JpaProperties();
}
private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(jpaProperties.isShowSql());
adapter.setDatabase(jpaProperties.getDatabase());
adapter.setDatabasePlatform(jpaProperties.getDatabasePlatform());
adapter.setGenerateDdl(jpaProperties.isGenerateDdl());
return adapter;
}
My session scoped class instead of context holder:
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PreferredDatabaseSession implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private DatabaseEnvironment preferredDb;
public DatabaseEnvironment getPreferredDb() {
return preferredDb;
}
public void setPreferredDb(DatabaseEnvironment preferredDb) {
this.preferredDb = preferredDb;
}
}
If I understand your requirement correctly, you intend to define two data sources and for a given request you want to route your queries to a particular data source based on some condition.
The solution is:
File application.properties
#database1 properties
app.database1.connection.url = url1
app.database1.connection.username = username1
app.database1.connection.password = password1
#database2 properties
app.database2.connection.url = url2
app.database2.connection.username = username2
app.database2.connection.password = password2
#default
default.datasource.key=dataSource1
File CommonRoutingDataSource.java
public class CommonRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceName();
}
public void initDataSources(final DataSource dataSource1, final DataSource dataSource2,
final String defaultDataSourceKey) {
final Map<Object, Object> dataSourceMap = new HashMap<Object, Object>();
dataSourceMap.put("dataSource1", dataSource1);
dataSourceMap.put("dataSource2", dataSource2);
this.setDefaultTargetDataSource(dataSourceMap.get(defaultDataSourceKey));
this.setTargetDataSources(dataSourceMap);
}
}
File DataSourceContextHolder.java
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
private DataSourceContextHolder() {
// Private no-op constructor
}
public static final void setDataSourceName(final String dataSourceName) {
Assert.notNull(dataSourceName, "dataSourceName cannot be null");
contextHolder.set(dataSourceName);
}
public static final String getDataSourceName() {
return contextHolder.get();
}
public static final void clearDataSourceName() {
contextHolder.remove();
}
}
File DataSourceConfig.java
public class DataSourceConfig {
#Autowired
private Environment env;
#Autowired
#Bean(name = "dataSource")
public DataSource getDataSource(final DataSource dataSource1, final DataSource dataSource2) {
final CommonRoutingDataSource dataSource = new CommonRoutingDataSource();
dataSource.initDataSources(dataSource1, dataSource2, env.getProperty("default.datasource.key"));
return dataSource;
}
#Bean(name = "dataSource1")
public DataSource getDataSource1() throws SQLException {
// The exact DataSource class imported shall be as per your requirement - HikariCP, or Tomcat etc.
final DataSource dataSource = new DataSource();
dataSource.setDriverClassName();
dataSource.setUrl(env.getProperty("app.database1.connection.url"));
// Set all data source attributes from the application.properties file
return dataSource;
}
#Bean(name = "dataSource2")
public DataSource getDataSource2() throws SQLException {
// The exact DataSource class imported shall be as per your requirement - HikariCP, or Tomcat etc.
final DataSource dataSource = new DataSource();
dataSource.setDriverClassName();
dataSource.setUrl(env.getProperty("app.database2.connection.url"));
// set all data source attributes from the application.properties file
return dataSource;
}
}
Now, somewhere in your code (either an Aspect or Controller), you need to dynamically set the data source conditionally:
DataSourceContextHolder.setDataSourceName("dataSource1");
Note: It's better to declare the data source names as enums rather than strings "dataSource1", "dataSource2", etc.
The below snippet works for me
first.datasource.jdbc-url=jdbc-url
first.datasource.username=username
first.datasource.password=password
.
.
.
.
=================== In Java Configuration File ==================
#Primary
#Bean(name = "firstDataSource")
#ConfigurationProperties(prefix = "first.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "firstEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean barEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("firstDataSource") DataSource dataSource) {
Map<String, String> props = new HashMap<String, String>();
props.put("spring.jpa.database-platform", "org.hibernate.dialect.Oracle12cDialect");
.
.
.
return builder.dataSource(dataSource).packages("com.first.entity").persistenceUnit("firstDB")
.properties(props)
.build();
}
#Primary
#Bean(name = "firstTransactionManager")
public PlatformTransactionManager firstTransactionManager(
#Qualifier("firstEntityManagerFactory") EntityManagerFactory firstEntityManagerFactory) {
return new JpaTransactionManager(firstEntityManagerFactory);
}
second.datasource.jdbc-url=jdbc-url
second.datasource.username=username
second.datasource.password=password
.
.
.
.
=================== In Java Configuration File ==================
#Bean(name = "secondDataSource")
#ConfigurationProperties(prefix = "second.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "secondEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean barEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("secondDataSource") DataSource dataSource) {
Map<String, String> props = new HashMap<String, String>();
props.put("spring.jpa.database-platform", "org.hibernate.dialect.Oracle12cDialect");
.
.
.
return builder.dataSource(dataSource).packages("com.second.entity").persistenceUnit("secondDB")
.properties(props)
.build();
}
#Bean(name = "secondTransactionManager")
public PlatformTransactionManager secondTransactionManager(
#Qualifier("secondEntityManagerFactory") EntityManagerFactory secondEntityManagerFactory) {
return new JpaTransactionManager(secondEntityManagerFactory);
}
What we are doing: Update subscription for received business ID.
We receive business ID on activeMQ queue.
JMSListener picked business ID, find the all associated subscription for received business.
Process all subscription identify who need update (can be zero or more than one).
For each subscription we call service method udate(SubscriptionDTO subscriptionDTO). Find the subscription entity object. This update method update the entity object using dto object. Then we call flush method on it. Note : Update method is annotated with #transactional. Call flush method from service#udpate(dto) method.
We observed different behavior in windows vs Linux. On windows it worked well but on Linux we are getting failure after execution of flush method– javax.persistence.TransactionRequiredException: no transaction is in progress
We tried adding #Transactional on JMSListner but got same error on Linux. But when we tried without call of flush method error gone but no update happened. Subscription data is same as before.
Not sure how good problem was explained above.
Looking for guidance, much appreciated for response.
JPA Configuration
package edu.learn.config;
#Configuration
#EnableJpaRepositories(basePackages = "edu.learn.repositories", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
#EnableTransactionManagement
public class JpaConfiguration
{
private final String DEFAULT_TEST_QUERY = "SELECT 1";
#Autowired
private Environment environment;
#Value("${datasource.maxPoolSize:10}")
private int maxPoolSize;
/*
* Populate SpringBoot DataSourceProperties object directly from
* application.yml based on prefix.Thanks to .yml, Hierachical data is
* mapped out of the box with matching-name properties of
* DataSourceProperties object].
*/
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource")
public DataSourceProperties dataSourceProperties()
{
return new DataSourceProperties();
}
/*
* Configure HikariCP pooled DataSource.
*/
#Bean
public DataSource dataSource()
{
DataSourceProperties dataSourceProperties = dataSourceProperties();
HikariDataSource dataSource = (HikariDataSource) DataSourceBuilder.create()
.type(com.zaxxer.hikari.HikariDataSource.class).username(dataSourceProperties.getUsername())
.password(dataSourceProperties.getPassword()).build();
dataSource.setMaximumPoolSize(PropertyUtil.getMaxPoolSize(environment));
dataSource.setInitializationFailFast(PropertyUtil.getInitializationFailFast(environment));
dataSource.setMinimumIdle(PropertyUtil.getMinIdle(environment));
dataSource.setConnectionTestQuery(DEFAULT_TEST_QUERY);
dataSource.setConnectionTimeout(PropertyUtil.getConnectionTimeout(environment));
dataSource.setIdleTimeout(PropertyUtil.getIdleTimeout(environment));
dataSource.setMaxLifetime(PropertyUtil.getMaxLifetime(environment));
dataSource.setDataSourceClassName(PropertyUtil.getDataSourceClassName(environment));
if ("com.mysql.jdbc.jdbc2.optional.MysqlDataSource".equals(PropertyUtil.getDataSourceClassName(environment)))
{
dataSource.addDataSourceProperty("databaseName", PropertyUtil.defaultSchema(environment));
dataSource.addDataSourceProperty("cachePrepStmts", PropertyUtil.getCachePrepStmts(environment));
dataSource.addDataSourceProperty("prepStmtCacheSize", PropertyUtil.getPrepStmtCacheSize(environment));
dataSource.addDataSourceProperty(
"prepStmtCacheSqlLimit", PropertyUtil.getPrepStmtCacheSqlLimit(environment)
);
dataSource.addDataSourceProperty("useServerPrepStmts", PropertyUtil.getUseServerPrepStmts(environment));
dataSource.addDataSourceProperty("serverName", PropertyUtil.dbServerName(environment));
dataSource.addDataSourceProperty("portNumber", PropertyUtil.portNumber(environment));
}
return dataSource;
}
/*
* Entity Manager Factory setup.
*/
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException
{
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[]
{
"edu.learn.model"
});
factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
factoryBean.setJpaProperties(jpaProperties());
return factoryBean;
}
/*
* Provider specific adapter.
*/
#Bean
public JpaVendorAdapter jpaVendorAdapter()
{
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
return hibernateJpaVendorAdapter;
}
/*
* Here you can specify any provider specific properties.
*/
private Properties jpaProperties()
{
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("hibernate.globally_quoted_identifiers", "true");
properties.put("hibernate.hbm2ddl.auto", "validates");
properties.put("ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
properties.put("show_sql", "false");
properties.put("format_sql", "true");
return properties;
}
#Bean
#Autowired
public PlatformTransactionManager transactionManager(EntityManagerFactory emf)
{
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(emf);
return txManager;
}
static class PropertyUtil
{
final static String DEFAULT_CHARACTER_ENCODING = "utf-8";
final static String DEFAULT_DATASOURCE_CLASS = "com.mysql.jdbc.Driver";
public static boolean getCachePrepStmts(final Environment environment)
{
String cachePrepStmts = environment.getProperty("spring.datasource.hikari.cachePrepStmts");
if ("false".equalsIgnoreCase(cachePrepStmts))
{
return false;
}
return true;
}
public static int getPrepStmtCacheSize(final Environment environment)
{
String prepStmtCacheSize = environment.getProperty("spring.datasource.hikari.prepStmtCacheSize");
try
{
return Integer.parseInt(prepStmtCacheSize);
}
catch(Exception e)
{
return 250;
}
}
public static int getPrepStmtCacheSqlLimit(final Environment environment)
{
String prepStmtCacheSqlLimit = environment.getProperty("spring.datasource.hikari.prepStmtCacheSqlLimit");
try
{
return Integer.parseInt(prepStmtCacheSqlLimit);
}
catch(Exception e)
{
return 2048;
}
}
public static boolean getUseServerPrepStmts(final Environment environment)
{
String useServerPrepStmts = environment.getProperty("spring.datasource.hikari.useServerPrepStmts");
if ("false".equalsIgnoreCase(useServerPrepStmts))
{
return false;
}
return true;
}
public static boolean getInitializationFailFast(final Environment environment)
{
String initializationFailFast = environment.getProperty("spring.datasource.hikari.initializationFailFast");
if ("false".equalsIgnoreCase(initializationFailFast))
{
return false;
}
return true;
}
public static long getConnectionTimeout(final Environment environment)
{
String connectionTimeout = environment.getProperty("spring.datasource.hikari.connectionTimeout");
try
{
return Integer.parseInt(connectionTimeout);
}
catch(Exception e)
{
return TimeUnit.SECONDS.toMillis(60);
}
}
public static long getIdleTimeout(final Environment environment)
{
String idleTimeout = environment.getProperty("spring.datasource.hikari.idleTimeout");
try
{
return Integer.parseInt(idleTimeout);
}
catch(Exception e)
{
return TimeUnit.SECONDS.toMillis(60);
}
}
public static long getMaxLifetime(final Environment environment)
{
String maxLifetime = environment.getProperty("spring.datasource.hikari.maxLifetime");
try
{
return Integer.parseInt(maxLifetime);
}
catch(Exception e)
{
return TimeUnit.SECONDS.toMillis(90);
}
}
public static int getMinIdle(final Environment environment)
{
String minIdle = environment.getProperty("spring.datasource.hikari.minIdle");
try
{
return Integer.parseInt(minIdle);
}
catch(Exception e)
{
return 5;
}
}
public static int getMaxPoolSize(final Environment environment)
{
String maxPoolSize = environment.getProperty("spring.datasource.maxPoolSize");
try
{
return Integer.parseInt(maxPoolSize);
}
catch(Exception e)
{
return 25;
}
}
public static String getDataSourceClassName(final Environment environment)
{
String dataSourceClassName = environment.getProperty("spring.datasource.dataSourceClassName");
if (dataSourceClassName != null && "".equalsIgnoreCase(dataSourceClassName.trim()) == false)
{
return dataSourceClassName;
}
return DEFAULT_DATASOURCE_CLASS;
}
public static String getCharacterEncoding(final Environment environment)
{
String characterEncoding = environment.getProperty("spring.datasource.characterEncoding");
if (characterEncoding != null && "".equalsIgnoreCase(characterEncoding.trim()) == false)
{
return characterEncoding;
}
return DEFAULT_CHARACTER_ENCODING;
}
public static boolean getUniCode(final Environment environment)
{
String useUnicode = environment.getProperty("spring.datasource.useUnicode");
if ("false".equalsIgnoreCase(useUnicode))
{
return false;
}
return true;
}
public static String showSQL(final Environment environment)
{
String showSQL = environment.getProperty("spring.datasource.hibernate.showSQL");
if ("false".equalsIgnoreCase(showSQL))
{
return "false";
}
return "true";
}
public static String formatSQL(final Environment environment)
{
String formatSQL = environment.getProperty("spring.datasource.hibernate.format_sql");
if ("false".equalsIgnoreCase(formatSQL))
{
return "false";
}
return "true";
}
public static String dbServerName(final Environment environment)
{
String dbServerName = environment.getProperty("spring.datasource.serverName");
if (dbServerName == null || "".equalsIgnoreCase(dbServerName.trim()))
{
return "localhost";
}
return dbServerName;
}
public static int portNumber(final Environment environment)
{
String portNumber = environment.getProperty("spring.datasource.portNumber");
try
{
return Integer.parseInt(portNumber);
}
catch(Exception e)
{
return 3306;
}
}
public static String defaultSchema(final Environment environment)
{
String defaultSchema = environment.getProperty("spring.datasource.defaultSchema");
if (defaultSchema == null || "".equalsIgnoreCase(defaultSchema.trim()))
{
return "subscription";
}
return defaultSchema;
}
}
}
JMS Configuration
#EnableJms
#Configuration
public class JmsConfiguration
{
#Bean
public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer)
{
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
return factory;
}
}
Startup class
#Import({JpaConfiguration.class, JmsConfiguration.class})
#SpringBootApplication(scanBasePackages={"edu.learn"})
public class SubscriptionApiApplication
{
public static void main(String[] args)
{
SpringApplication.run(SubscriptionApiApplication.class, args);
}
}
JMS queue listener
#Component
public class OrderTransactionReceiver
{
private static final Logger LOGGER = LoggerFactory.getLogger(OrderTransactionReceiver.class);
#Autowired
private SubscriptionService subsriptionService;
#JmsListener(destination = "OrderTransactionQueue", containerFactory = "myFactory")
public void receiveMessage(String businessID)
{
List<SubscriptionDTO> subscriptions = subsriptionService.findByBusinessID(businessID);
for (SubscriptionDTO subscription : subscriptions)
{
subscription.setStatus("active");
subsriptionService.update(subscription);
}
}
}
Service
#Service
class RepositorySubscriptionService implements SubscriptionService
{
private static final Logger LOGGER = LoggerFactory.getLogger(RepositorySubscriptionService.class);
private final SubscriptionRepository repository;
#Transactional
#Override
public SubscriptionDTO update(SubscriptionDTO updatedSubscriptionEntry)
{
LOGGER.info(
"Updating the information of a subscription entry by using information: {}", updatedSubscriptionEntry
);
SubscriptionEntity updated = findSubscriptionEntryById(updatedSubscriptionEntry.getId());
updated.update(updatedSubscriptionEntry.getStatus());
// We need to flush the changes or otherwise the returned object
// doesn't contain the updated audit information.
repository.flush();
LOGGER.info("Updated the information of the subscription entry: {}", updated);
return SubscriptionMapper.mapEntityIntoDTO(updated);
}
}
Repostiory
public interface SubscriptionRepository extends
Repository<SubscriptionEntity, String>,
JpaSpecificationExecutor<SubscriptionEntity>
{
void delete(SubscriptionEntity deleted);
List<SubscriptionEntity> findAll();
Optional<SubscriptionEntity> findOne(String id);
List<SubscriptionEntity> findByBusinessID(final String businessID);
void flush();
SubscriptionEntity save(SubscriptionEntity persisted);
}
JTA transaction configuration
#Configuration
#EnableJpaRepositories(basePackages = "edu.learn.repositories", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
#EnableTransactionManagement
public class JpaConfiguration
{
private final String DEFAULT_TEST_QUERY = "SELECT 1";
#Autowired
private Environment environment;
#Value("${datasource.maxPoolSize:10}")
private int maxPoolSize;
/*
* Populate SpringBoot DataSourceProperties object directly from
* application.yml based on prefix.Thanks to .yml, Hierachical data is
* mapped out of the box with matching-name properties of
* DataSourceProperties object].
*/
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource")
public DataSourceProperties dataSourceProperties()
{
return new DataSourceProperties();
}
/*
* Configure HikariCP pooled DataSource.
*/
#Bean(initMethod = "init", destroyMethod = "close")
public DataSource dataSource()
{
DataSourceProperties dataSourceProperties = dataSourceProperties();
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setServerName(PropertyUtil.dbServerName(environment));
mysqlXaDataSource.setPort(PropertyUtil.portNumber(environment));
mysqlXaDataSource.setUser(dataSourceProperties.getUsername());
mysqlXaDataSource.setPassword(dataSourceProperties.getPassword());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setDatabaseName(PropertyUtil.defaultSchema(environment));
mysqlXaDataSource.setCachePreparedStatements(PropertyUtil.getCachePrepStmts(environment));
try
{
mysqlXaDataSource.setPrepStmtCacheSqlLimit(PropertyUtil.getPrepStmtCacheSqlLimit(environment));
mysqlXaDataSource.setPrepStmtCacheSize(PropertyUtil.getPrepStmtCacheSize(environment));
}
catch (SQLException e)
{
e.printStackTrace();
}
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("xaSubscription");
xaDataSource.setTestQuery(DEFAULT_TEST_QUERY);
xaDataSource.setMaxPoolSize(PropertyUtil.getMaxPoolSize(environment));
xaDataSource.setMaxIdleTime(PropertyUtil.getMinIdle(environment));
xaDataSource.setMaxLifetime((int)PropertyUtil.getMaxLifetime(environment));
xaDataSource.setBorrowConnectionTimeout((int)PropertyUtil.getConnectionTimeout(environment));
return xaDataSource;
}
/*
* Entity Manager Factory setup.
*/
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException
{
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[]
{
"edu.learn.model"
});
factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
factoryBean.setJpaProperties(jpaProperties());
return factoryBean;
}
/*
* Provider specific adapter.
*/
#Bean
public JpaVendorAdapter jpaVendorAdapter()
{
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
return hibernateJpaVendorAdapter;
}
/*
* Here you can specify any provider specific properties.
*/
private Properties jpaProperties()
{
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("hibernate.globally_quoted_identifiers", "true");
properties.put("hibernate.hbm2ddl.auto", "validates");
properties.put("ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
properties.put("show_sql", "false");
properties.put("format_sql", "true");
properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
properties.put("javax.persistence.transactionType", "JTA");
return properties;
}
#Bean
public UserTransaction userTransaction() throws Throwable
{
UserTransactionImp userTransactionImp = new UserTransactionImp();
userTransactionImp.setTransactionTimeout(1000);
return userTransactionImp;
}
#Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
public TransactionManager atomikosTransactionManager() throws Throwable
{
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setForceShutdown(false);
AtomikosJtaPlatform.transactionManager = userTransactionManager;
return userTransactionManager;
}
#Bean(name = "transactionManager")
#DependsOn({ "userTransaction", "atomikosTransactionManager" })
public PlatformTransactionManager transactionManager() throws Throwable
{
UserTransaction userTransaction = userTransaction();
AtomikosJtaPlatform.transaction = userTransaction;
TransactionManager atomikosTransactionManager = atomikosTransactionManager();
return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}
}
public class AtomikosJtaPlatform extends AbstractJtaPlatform
{
private static final long serialVersionUID = 1L;
static TransactionManager transactionManager;
static UserTransaction transaction;
#Override
protected TransactionManager locateTransactionManager()
{
return transactionManager;
}
#Override
protected UserTransaction locateUserTransaction()
{
return transaction;
}
}
Recently in a interview, It was asked from me that is it possible to have Spring Application or Hibernate Application without XML configuration..?
So what could be the best answer of it, Please explain.
Yes you can, e.g:-> Account
#Entity
public class Account {
#Id
#GeneratedValue
private Long id;
#Temporal(TemporalType.DATE)
private Data data;
private Long accountNumber;
private String owner;
private double balance;
public Long getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(Long accountNumber) {
this.accountNumber = accountNumber;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
#Override
public String toString() {
return "Account{" +
"accountNumber=" + accountNumber +
", owner='" + owner + '\'' +
", balance=" + balance +
'}';
}
}
Dao:
#Repository
public class AccountDao {
#PersistenceContext
EntityManager entityManager;
#Transactional
public void save(Account account) {
entityManager.persist(account);
}
protected Account getByKey(Long key) {
return (Account) entityManager.find(Account.class, key);
}
}
Configuration->
#Configuration
#EnableTransactionManagement
#ComponentScan("your scan package path")
public class AppConfig {
#Bean
public DataSource dataSource() {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setUsername("root");
driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/your_data_base_name");
driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
driverManagerDataSource.setPassword("password");
return driverManagerDataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
localContainerEntityManagerFactoryBean.setDataSource(dataSource());
localContainerEntityManagerFactoryBean.setJpaVendorAdapter(getHibernateJpaVendorAdapter());
localContainerEntityManagerFactoryBean.setJpaProperties(additionalProperties());
localContainerEntityManagerFactoryBean.setPackagesToScan("entity_package_name");
return localContainerEntityManagerFactoryBean;
}
#Bean
public JpaVendorAdapter getHibernateJpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
return hibernateJpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(entityManagerFactory().getObject());
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("hibernate.hbm2ddl.auto", "create");
properties.put("hibernate_show_sql", true);
return properties;
}
}
Test class :
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
Account account = new Account();
account.setAccountNumber(123L);
account.setBalance(100056.5);
AccountService accountService = applicationContext.getBean(AccountService.class);
accountService.save(account);
}
}
I have one issue. I integrate my app with Activiti (in the same DB). When I insert, update or delete my entities (not entities's Activiti) by Dao class have use #Transactional but nothing about is being saved to database with no exception.
Here is my config to integrating:
#Configuration
public class ActivitiEngineConfiguration {
private final Logger log = LoggerFactory.getLogger(ActivitiEngineConfiguration.class);
#Autowired
protected Environment environment;
#Bean
public DataSource dataSource() {
SimpleDriverDataSource ds = new SimpleDriverDataSource();
try {
#SuppressWarnings("unchecked")
Class<? extends Driver> driverClass = (Class<? extends Driver>) Class.forName(environment.getProperty("jdbc.driver", "org.postgresql.Driver"));
ds.setDriverClass(driverClass);
} catch (Exception e) {
log.error("Error loading driver class", e);
}
ds.setUrl(environment.getProperty("spring.datasource.url"));
ds.setUsername(environment.getProperty("spring.datasource.username"));
ds.setPassword(environment.getProperty("spring.datasource.password"));
return ds;
}
#Bean(name = "transactionManager")
public PlatformTransactionManager annotationDrivenTransactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
System.out.println("transactionManager: "+transactionManager);
return transactionManager;
}
#Bean(name="processEngineFactoryBean")
public ProcessEngineFactoryBean processEngineFactoryBean() {
ProcessEngineFactoryBean factoryBean = new ProcessEngineFactoryBean();
factoryBean.setProcessEngineConfiguration(processEngineConfiguration());
return factoryBean;
}
#Bean(name="processEngine")
public ProcessEngine processEngine() {
// Safe to call the getObject() on the #Bean annotated processEngineFactoryBean(), will be
// the fully initialized object instanced from the factory and will NOT be created more than once
try {
return processEngineFactoryBean().getObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#Bean(name="processEngineConfiguration")
public ProcessEngineConfigurationImpl processEngineConfiguration() {
SpringProcessEngineConfiguration processEngineConfiguration = new SpringProcessEngineConfiguration();
processEngineConfiguration.setDataSource(dataSource());
processEngineConfiguration.setDatabaseSchemaUpdate(environment.getProperty("engine.schema.update", "true"));
processEngineConfiguration.setTransactionManager(annotationDrivenTransactionManager());
processEngineConfiguration.setJobExecutorActivate(Boolean.valueOf(
environment.getProperty("engine.activate.jobexecutor", "false")));
processEngineConfiguration.setAsyncExecutorEnabled(Boolean.valueOf(
environment.getProperty("engine.asyncexecutor.enabled", "true")));
processEngineConfiguration.setAsyncExecutorActivate(Boolean.valueOf(
environment.getProperty("engine.asyncexecutor.activate", "true")));
processEngineConfiguration.setHistory(environment.getProperty("engine.history.level", "full"));
String mailEnabled = environment.getProperty("engine.email.enabled");
if ("true".equals(mailEnabled)) {
processEngineConfiguration.setMailServerHost(environment.getProperty("engine.email.host"));
int emailPort = 1025;
String emailPortProperty = environment.getProperty("engine.email.port");
if (StringUtils.isNotEmpty(emailPortProperty)) {
emailPort = Integer.valueOf(emailPortProperty);
}
processEngineConfiguration.setMailServerPort(emailPort);
String emailUsernameProperty = environment.getProperty("engine.email.username");
if (StringUtils.isNotEmpty(emailUsernameProperty)) {
processEngineConfiguration.setMailServerUsername(emailUsernameProperty);
}
String emailPasswordProperty = environment.getProperty("engine.email.password");
if (StringUtils.isNotEmpty(emailPasswordProperty)) {
processEngineConfiguration.setMailServerPassword(emailPasswordProperty);
}
}
// List<AbstractFormType> formTypes = new ArrayList<AbstractFormType>();
// formTypes.add(new UserFormType());
// formTypes.add(new ProcessDefinitionFormType());
// formTypes.add(new MonthFormType());
// processEngineConfiguration.setCustomFormTypes(formTypes);
return processEngineConfiguration;
}
#Bean
public RepositoryService repositoryService() {
return processEngine().getRepositoryService();
}
#Bean
public RuntimeService runtimeService() {
return processEngine().getRuntimeService();
}
#Bean
public TaskService taskService() {
return processEngine().getTaskService();
}
#Bean
public HistoryService historyService() {
return processEngine().getHistoryService();
}
#Bean
public FormService formService() {
return processEngine().getFormService();
}
#Bean
public IdentityService identityService() {
return processEngine().getIdentityService();
}
#Bean
public ManagementService managementService() {
return processEngine().getManagementService();
}
}
DAO Layer:
#Autowired
private SessionFactory sessionFactory;
#Override
#Transactional
public void save(MyEntity obj) {
sessionFactory.getCurrentSession().saveOrUpdate(loaiDanhMuc);
}
Thank all!
I guess #Transactional is not working in this case
Please check following things below:
Check if <tx:annotation-driven /> is defined in your spring xml file.