Auto reconnect JPA EntityManager after connection loss - java

I have an EntityManager bean object called in service classes as autowired object.
Spring Config class:
#EnableWebMvc
#Configuration("myWebConfiguration")
#ComponentScan(basePackages = {"org.my.app"})
#EnableScheduling
public class MyWebConfiguration extends WebMvcConfigurerAdapter {
....
private static EntityManager entityManager;
#Bean
public EntityManager entityManager() {
if (entityManager == null) {
entityManager = managerFactoryBean().getObject().createEntityManager();
}
return entityManager;
}
....
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "update");
properties.setProperty("hibernate.show_sql", "true");
properties.setProperty("hibernate.format_sql", "true");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQL9Dialect");
return properties;
}
....
}
Sample Service class:
#Service("sampleService")
public class SampleService {
#Autowired
protected EntityManager entityManager;
public Object find(Class entityClass, Object id) {
return entityManager.find(entityClass, id);
}
....
}
And the problem:
If connection between WebApp server and DB server get lost, JPA and spring cannot reconnect to the DB Server and calling entityManager methods causes exceptions such as org.postgresql.util.PSQLException: This connection has been closed. and org.hibernate.exception.JDBCConnectionException: could not inspect JDBC autocommit mode.
Is it possible to detect connection loss automatically and re-establishing the connection in case of connection loss?

That is possible and that is a responsibility fulfilled by connection pooling (or datasource that supports connection pooling) libraries.
For example, If you use DBCP, you can set parameters like validationQuery="SELECT 1" and testOnBorrow="true" that allows detect the connection status and reopen if needed.
Similarly, c3p0 allows to configure such parameters using c3p0.testConnectionOnCheckout=true etc.
Other connection pooling libraries would expose similar configuration.
If you are already using a connection pooling library (or datasource that uses connection pool) you may want to check its documentation to get the relevant configuration properties to be set. If not, you may want to consider using a connection pooling library or a data source that uses connection pooling and exposes those parameters.
UPDATE:
For c3p0 can you add the following properties and test:
properties.setProperty("hibernate.c3p0.preferredTestQuery","SELECT 1");
properties.setProperty("hibernate.c3p0.testConnectionOnCheckout","true");

Related

Multiple connection pools with shared TransactionManager for the same database in Spring?

I'm using Spring JPA to connect to a relational database. I want to have multiple connection pools connecting to the same database so scheduled tasks can't clog up the connection pool for user interactions.
This works fine if I define multiple DataSources like it is described in Spring JPA Hikari multiple connection pool with same datasource. Currently I define multiple different EntityManagerFactoryBeans and PlatformTransactionManagers, one for each DataSource. But I would like to be able to use the same physical transaction spanning calls to different JpaRepositories backed by different DataSources.
#Service
public class FooService {
// ..
#Transactional
public saveFoo(Foo foo) {
fooRepository.save(foo);
barService.somethingElse(foo);
}
}
#Service
public class BarService {
#Transactional(propagation = Propagation.MANDATORY)
public somethingElse(Foo foo) {
// BarRepository is backed by another DataSource as FooRepository,
// but both DataSources connect to the same database
barRepository.saveAndFlush(new FoobarEvent(foo));
}
}
The call to BarRepository fails for me with:
InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
It seems like multiple different DataSources are needed because the DataSources define the connection pool. But since the PlatformTransactionManager and the EntityManagerFactoryBean references the DataSource it's not really possible to have transactions span multiple repositories that are backed by different DataSources because Spring doesn't know that both DataSources point to the same database. Is there a way to configure Spring JPA to achieve what I want?
If your service is running through an interface from your controller, then Transactional method should come on the interface layer method.
And use javax.persistance.Transactional class for the annotation instead of org.springframework.transaction.annotation.Transactional
and specify #EnableTransactionManagement in your application class to enable transactions.

How to configure #Transactional support to Redis in Springboot?

I am currently working on a project which is using Redis mainly for caching purposes, I am using Oracle as the main database and Spring Data JPA to handle the database layer in my project. I need to know how to use #Transactional annotation support to handle transactions in Redis. I have already referred to lots of tutorials and documentation regarding this scenario. In most of those tutorials, there is always the same set of source codes available. But still, I didn't have a clear idea about the implementation. Because in my application there is already a data source available which I configured through property file. (Oracle database) So I doubt the implementation of the dataSource bean. And I couldn't understand the usage of transactionManager bean too. How should I implement this properly please give a detailed explanation.
Source code which I found on the internet.
#Configuration
#EnableTransactionManagement // 1
public class RedisConfig {
#Bean
public StringRedisTemplate stringRedisTemplate(LettuceConnectionFactory redisConnectionFactory) {
// Configure redisTemplate
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
/ / Open transaction support
stringRedisTemplate.setEnableTransactionSupport(true); // 2
return stringRedisTemplate;
}
#Bean
public PlatformTransactionManager transactionManager() throws SQLException {
return new DataSourceTransactionManager(dataSource()); // 3
}
#Bean
public DataSource dataSource() throws SQLException {
// ...
}
}
Updated :
Currently configured datasource properties in apppication.properties file.
# OracleDB connection settings
spring.datasource.url=jdbc:oracle:thin:#192.168.20.108:1521:orcl
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# HikariCP settings
spring.datasource.hikari.minimumIdle=5
spring.datasource.hikari.maximumPoolSize=20
spring.datasource.hikari.idleTimeout=30000
spring.datasource.hikari.maxLifetime=2000000
spring.datasource.hikari.connectionTimeout=30000
spring.datasource.hikari.poolName=redis-sample-pool
Resource about the Redis Transactions handling
There're two ways to configure DataSource
Using the config file
spring.datasource.url= jdbc:oracle:thin:#//localhost
spring.datasource.username=test
spring.datasource.password=test1234
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
#hibernate config dialect for JPA, choose dialect based on the db version
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
You can create manually the same by reading these properties, with the help of DataSourceBuilder
#Bean
public DataSource dataSource(){
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("oracle.jdbc.OracleDriver");
dataSourceBuilder.url("jdbc:h2:mem:test");
dataSourceBuilder.username("test");
dataSourceBuilder.password("test1234");
return dataSourceBuilder.build();
}

How to kill Hibernates database connections in Spring boot 1.5.2?

The problem is simple when I use Spring boot 1.5.2 with Hibernate to connect to a datasource with configued like this:
#Bean
public DataSource dataSource() {
DataSourceBuilder = datasourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.url(DATASOURCE_URL);
dataSourceBuilder.username(DATASOURCE_USERNAME);
dataSourceBuilder.password(DATASOURCE_PASSWORD);
return dataSourceBuilder.build();
}
and this is a Command line application, so when the application is almost done, I just want to close the Hibernate connections to the database and rename the database with JDBC. However, I don't know how to do this in Spring boot, any idea?
I tried to inject the DataSource object to a class to rename database but it cannot close the connections to database.
#Autowired
private DataSource dataSource;
public void closeConnection() {
dataSource.close();
}
with error
org.postgresql.util.PSQLException: ERROR: database "DATABASE" is being accessed by other users
Detail: There are 10 other sessions using the database.
You can use pg_terminate_backend() to kill a connection. You have to be superuser to use this function. This works on all operating systems the same.
refer this stackoverflow link :
https://stackoverflow.com/a/5109190/7801800

Spring JdbcTemplate alter session

I want to alter Oracle session for every connection that I get from the connection pool.
I found that it can be done by simply execute a statement. See here.
Is there a way to hook into the jdbc template or the datasource and execute a statement after the connection pool creates a new connection.
I'm using Spring Boot and creating the datasource that way:
#Bean
#ConfigurationProperties(prefix="datasource.local")
public DataSource localDataSource() {
return DataSourceBuilder.create().build();
}
There are a lot of ways to do so.
The first one:
DataSource is an interface, so why don't you implement it yourself (use Proxy pattern)? Create something like this:
class MyDataSource implements DataSource {
private DataSource realDataSource;
public Connection getConnection() {
Connection c = realDataSource.getConnection();
// do whatever you want to do and
return c;
}
}
All other methods will delegate directly to realDataSource.
This proxy can be used in a provided code snippet.
You can use some AOP - just provide an advice that after get connection is created will run and do whatever you need there. Basically it's the same proxy but automatically created by Spring.

Dynamic creation of EntityManager

My problem is actually simple but I really do not find a good solution to it.
I've currently to manage several DB in my application:
one UNIQUE admin DB (with a static name);
one client DB, with a name depending on the client.
I'm using JPA and I would like to create dynamically EntityManagers for client DB. But when I create this programmatically, I get this error:
javax.persistence.TransactionRequiredException: joinTransaction has been called on a resource-local EntityManager which is unable to register for a JTA transaction.
Here is the code:
#Stateful
public class ServiceImpl implements Service{
private EntityManagerFactory emf;
private EntityManager em;
#PostConstruct // automatically called when EJB constructed and session starts
public void init() {
emf = Persistence.createEntityManagerFactory("punit");
em = emf.createEntityManager();
}
...
And
#Stateful(mappedName = "CustomerService")
public class CustomerServiceImpl extends ServiceImpl implements CustomerService {
#Override
public void create(Customer cust) {
getEm().joinTransaction();
getEm().persist(cust);
}
More generally, I'v got problems with JPA. I just would like to connect to two databases, do some CRUD operations on them. But I really don't know how to manage transactions (my first approach was to let the container manage it...).
If someone could help me, could be great!
NB: I'm using a Glassfish Java EE server and PGSql DBs.
In jpa, you can declare several prsistenceunits in the persistence.xml file.
At the point of injection, you can do something like this:
#PersistenceContext(unitName = "unitName0", properties={#PersistenceProperty(...)}
EntityManager emClient;
#PersistenceContext(unitName = "unitName1", properties={#PersistenceProperty(...)}
EntityManager emAdmin;
This way, you dont have to create the entity managers manually, hence you get the container transaction management.
NOT TESTED:
If you have dynamic database names, you would inject EntityManagerFactory
#PersistenceContext(unitName ="name")
EntityManagerFactory emf;
//at the point you want the EntityManager
Map<String, String> props; //put the connection property for the EM here
EntityManager em = emf.createEntityManager(props);
Based on the fact that in a J2EE environment we use the concept of DataSources, and ConnectionPooling, it would be nearly impossible to implement this kind of dynamic datasources, without resorting to manual creation of entitymanagerfactory.
This is my reasoning:
The server manages the connection pooling, and the jpa provider (such as eclipselink) uses jndi to determine the connection to the database. This implies that if you were to change the database name, then it must also have a connection pooling resources, and an associated jdbc resource. This will ofcourse negate what you want to do.
Basic solution: Create EntityManagerFactory manually and manually manage transactions.
Specify in the persistence xml that the unit is non-jta for this to work.
Then you can programmatically supply connection data based on user-session:
Something of this sort:
//this must be session specific.
class PersistenceSession{
static Map<String, String> clientSessionProps;
//When new session starts and a new client has logged in.
static void setClientConnectionProperties(Client client){
.....
}
static Map<String, String> getClientSessionProps(){
return clientSessionProps;
}
}
At the ejb level.
#Stateless
public class TestEntityFacade extends AbstractFacade<TestEntity> {
private EntityManagerFactory emf;
#PostConstruct
void init(){
emf = Persistence.createEntityManagerFactory("name");
}
#Override
protected EntityManager getEntityManager() {
return emf.createEntityManager(PersistenceSession.getClientSessionProps());
}
public TestEntityFacade() {
super(TestEntity.class);
}
void add(Entity e){
EntityManager em = getEntityManager();
em.getTransaction().begin();
.....
em.getTransaction().commit();
}
}
Actually very neat way how to do this is with CDI Producers, you can define a bean which will produce for your any number of custom Entity Managers, see the example.
#SessionScoped
public class EntityManagerProducer {
#Produces
#AdminDB
#PersistenceContext(unitName = "adminDB")
public EntityManaged adminDB;
#Produces
#UserDB
#PersistenceContext(unitName = "userDB")
public EntityManaged userDB;
}
This means that these two EntityManagers get created within every user session and than you can inject them to any bean with
#Inject
#UserDB
private EntityManager em; //if you want to use UserDB now
#UserDB and #AdminDB are your own defined Qualifiers. This leads to much more easier and readable code.

Categories