Perform VACUUM FULL with JPA - java

I'm using a PostgreSQL DB and I would like to start VACUUM FULL using JPA EntityManager.
Version 1
public void doVacuum(){
entityManager.createNativeQuery("VACUUM FULL").executeUpdate()
}
throws TransactionRequiredException
Version 2
#Transactional
public void doVacuum(){
entityManager.createNativeQuery("VACUUM FULL").executeUpdate()
}
throws PersistenceException "VACUUM cannot run inside a transaction block"
Version 3
public void doVacuum(){
entityManager.createNativeQuery("VACUUM FULL").getResultList()
}
vacuum is performed but after that I get PersistenceException "No results"
What is the correct way to start this sql command?

As Alay Hay mentioned, using the underlying connection will work:
public void doVacuum(){
org.hibernate.Session session = entityManager.unwrap(org.hibernate.Session);
org.hibernate.internal.SessionImpl sessionImpl = (SessionImpl) session; // required because Session doesn't provide connection()
java.sql.Connection connection = sessionImpl.connection();
connection.prepareStatement("VACUUM FULL").execute();
}

Here is a solution that does not require cast to internal implementation of Hibernate Session. Please keep in mind that VACUUM cannot be run in transaction block, this is why you need to set autoCommit to true.
Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
connection.setAutoCommit(true);
connection.prepareStatement("VACUUM FULL").execute();
connection.setAutoCommit(false);
}
});

Related

Hibernate Session/EntityManager is closed after commit()

I have just learnt how to use Hibernate today, and I am messing with a small database.
public class HibernateQuery {
private static final SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
private static final Session session = sessionFactory.getCurrentSession();
public static void main(String[] args) {
queryA();
queryB();
session.close();
sessionFactory.close();
}
public static void queryA() {
Transaction tx = session.beginTransaction();
List<GradesJPA> lst = session.createQuery("from GradesJPA", GradesJPA.class).list();
for(GradesJPA each : lst) {
System.out.println(each);
}
tx.commit();
}
public static void queryB() {
Transaction tx = session.beginTransaction();
List<String> lst = session.createQuery("select g.className from GradesJPA g", String.class).list();
for(String each : lst) {
System.out.println(each);
}
tx.commit();
}
}
I get the error Session/EntityManager is closed, after successfully finishing the first query. The solution to the problem is simple. Declare a session for each query(), instead of for the class. However, can someone please explain to me why? (Also, can you please tell me if I need to close the individual sessions created in each method?)
From what I have read, committing a transaction does not close the session: Does committing transaction close session?
From the docs, the only thing I understand is the session-per-operation anti-pattern, but somehow, I am forced to open a new session after each query().
There is also this suggestion, session/entitymanager is closed, and another on the Hibernate forum that advises against class-wide session because of concurrency problems. But I suspect that my code is not running in parallel with anything else.
This is the whole error printed:
Exception in thread "main" java.lang.IllegalStateException: Session/EntityManager is closed
at org.hibernate.internal.AbstractSharedSessionContract.checkOpen(AbstractSharedSessionContract.java:360)
at org.hibernate.engine.spi.SharedSessionContractImplementor.checkOpen(SharedSessionContractImplementor.java:139)
at org.hibernate.internal.AbstractSharedSessionContract.beginTransaction(AbstractSharedSessionContract.java:465)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:350)
at com.sun.proxy.$Proxy33.beginTransaction(Unknown Source)
at hibernate.HibernateQuery.queryB(HibernateQuery.java:38)
at hibernate.HibernateQuery.main(HibernateQuery.java:18)
Thank you.

JPA: How to set MySQL session variables?

I need to set a mysql session variable for my application to work with a MariaDB Galera Cluster as expected. The SQL call is: SET SESSION wsrep_sync_wait = 1. It shall be set at all times when the application uses the database. I am using EclipseLink as the JPA provider.
My question is: What is the best way to achieve this?
Option 1: EclipseLink Session Customizer
Register a session customizer in persistence.xml:
public class SessionCustomizerImpl implements org.eclipse.persistence.config.SessionCustomizer {
private final static String WSREP_SYNC_WAIT_CHECK_SQL = "SHOW SESSION VARIABLES LIKE 'wsrep_sync_wait'";
private final static String WSREP_SYNC_WAIT_SET_SQL = "SET SESSION wsrep_sync_wait = 1";
#Override
public void customize(Session session) throws Exception {
Vector result = session.executeSQL(WSREP_SYNC_WAIT_CHECK_SQL);
if ((result != null) && !result.isEmpty()) {
session.executeNonSelectingSQL(WSREP_SYNC_WAIT_SET_SQL);
// Galera connection detected; wsrep_sync_wait set to 1
} else {
// No Galera connection detected; wsrep_sync_wait not set
}
}
}
This does not work for me. Querying the session variable from an EntityManager returns a value of 0.
Option 2: EntityManager factory
Every time a new EntityManager is created, the SQL is executed.
public class SyncWaitEntityManagerFactory implements Factory<EntityManager> {
private final EntityManagerFactory emf;
#Inject
public SyncWaitEntityManagerFactory(EntityManagerFactory emf) {
this.emf = emf;
}
#Override
public EntityManager provide() {
final EntityManager em = emf.createEntityManager();
// set it
em.getTransaction().begin();
em.createNativeQuery("SET SESSION wsrep_sync_wait = 1").executeUpdate();
em.getTransaction().commit();
return em;
}
#Override
public void dispose(EntityManager instance) {
if (instance.isOpen()) {
instance.close();
}
}
}
This works, but I'm not sure if it is overkill. Also, I am worried about the cost of the transaction, which is only required by Query#executeUpdate(), but not by the actual SQL call.
Option 3: Via JDBC URL
Appending the variable and value to the JDBC URL (see here for details):
String jdbcUrl = "jdbc:mysql://db.example.test:3306/"+ JDBC_DB
+"?sessionVariables=wsrep_sync_wait=1";
Properties p = new Properties();
p.put("javax.persistence.jdbc.url", jdbcUrl);
p.put("javax.persistence.jdbc.user", JDBC_USER);
p.put("javax.persistence.jdbc.password", JDBC_PASSWORD);
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPU", p);
EntityManager entityManager = emf.createEntityManager();
Nice solution. Works for me; no effort, no transaction necessary. Downside: I can't catch exceptions (example: check first if the variable exists, then set it -- allows deployment of the code on systems that don't support/use this specific variable).
You could also use an aspect to execute a query every time a getConnection() is called which is for every transaction basically (the aspect is set after the call so that we have a valid connection object):
#Component
#Aspect
public class CustomConnectionPreparer implements ConnectionPreparer
{
#AfterReturning(pointcut = "execution(* *.getConnection(..))", returning = "connection")
public Connection prepare(Connection connection) throws SQLException {
// execute the query (also exception handling)
try (Statement statement = connection.createStatement()) {
statement.execute("SET SESSION wsrep_sync_wait = 1");
} catch (SQLException e) {
throw e;
}
return connection;
}
}
And before you return the connection to the caller you execute your query and you should always have that value set.

How to find out the transaction isolation level used for the particular query in hibernate?

I am using Spring MVC and hibernate. My underlying database is Sybase ASA. Inside my DAO I want to find out what is the transaction level used for the query
This is what my DAO has.
Session session = getSession();
String SQL_QUERY = "select ..... ";
Query query = session.createSQLQuery(SQL_QUERY);
query.executeUpdate();
I referred to this link but this seems to be outdated so doesn't work
What is default isolation level hibernate uses if not explicitely set?
I would say that question is raised incorrectly: a query itself doesn't have any isolation level. An Isolation level is a feature of a transaction.
So you, most probably, want to know what is the isolation level of current transaction? Hence you, most probably, do not manage this level by your code. In this case it is managed by DBMS default or current settings.
One of possible ways to explore it is described here:
try {
session = getSessionFactory().openSession();
txn = session.beginTransaction();
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
LOGGER.debug("Transaction isolation level is {}", Environment.isolationLevelToString(connection.getTransactionIsolation()));
}
});
txn.commit();
} catch (RuntimeException e) {
if ( txn != null && txn.isActive() ) txn.rollback();
throw e;
} finally {
if (session != null) {
session.close();
}
}

How to get java.sql.Connection object from Hibernate Session

I have requirement where I need java.sql.Connection. But I am using Hibernate here. Somehow I researched and I found below alternative but that's not working.
import org.hibernate.connection.ProxoolConnectionProvider;
public class ConnectionDB{
//I have imported below class
ProxoolConnectionProvider proxoolProvider = new ProxoolConnectionProvider();
org.hibernate.cfg.Configuration cfg = HibernateUtil.getConfiguration();//this method will return configuration
java.util.Properties props = cfg.getProperties();//This will return Properties Object
//Using properties object I just tried to get The Connection Object by following method
proxoolConn.configure(props);// I just configured the Porperties object
proxoolConn.getConnection();
}
But no luck I just end-up with no exception in console .. I am using Struts 2, Hibernate and JasperReports.
Can any one help me to get the connection object from Hibernate?
The following code assumes you have an existing and open Hibernate org.hibernate.Session:
public void doWorkOnConnection(Session session) {
session.doWork(new Work() {
public void execute(Connection connection) throws SQLException {
//use the connection here...
}
});
}
Should you need information on how to use the above classes, please read the Hibernate 3.5 Javadoc. Specifically, read Session and then Work.
You can also downcast the Session method into a SessionImpl and get the connection object easily:
SessionImpl sessionImpl = (SessionImpl) session;
Connection conn = sessionImpl.connection();

Get hold of a JDBC Connection object from a Stateless Bean

In a Stateless Session Bean an EntityManager is injected but I would like to get hold of a Connection object in order to invoke a DB Procedure.
Is there any solution to this ?
This is going to be JPA provider specific code. Typically this is done by invoking unwrap() on the EntityManager class.
If you are using EclipseLink, the following code (from the EclipseLink wiki) will be useful (in the case you are using an application-managed EntityManager) :
JPA 2.0
entityManager.getTransaction().begin();
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class); // unwraps the Connection class.
...
entityManager.getTransaction().commit();
JPA 1.0
entityManager.getTransaction().begin();
UnitOfWork unitOfWork = (UnitOfWork)((JpaEntityManager)entityManager.getDelegate()).getActiveSession();
unitOfWork.beginEarlyTransaction();
Accessor accessor = unitOfWork.getAccessor();
accessor.incrementCallCount(unitOfWork.getParent());
accessor.decrementCallCount();
java.sql.Connection connection = accessor.getConnection();
...
entityManager.getTransaction().commit();
Note, that the solution provided for JPA 2.0 will fail for Hibernate 3.6.5 with a PersistenceException containing the message
Hibernate cannot unwrap interface java.sql.Connection
Use the code provided by Skaffman to get it to work against Hibernate (verified to work under 3.6.5 even for container managed persistence contexts).
However, the EclipseLink wiki points out one useful bit of info - if you are using JTA managed datasources, you should be injecting it using the #Resource annotation or retrieving it using a JNDI lookup. As long as you need to perform transactional work against the database, it is immaterial as to whether you are obtaining a new connection from the data source or an existing one; most connection pools will anyway provide the same connection that is associated with the current thread (i.e. the one already used by the entity manager). You would therefore avoiding unwrapping the entity manager this way, and also perform transactional activity against the database; do remember that the persistence context cache, and a second-level cache may not be synchronized if you do this.
In Hibernate, the solution posted by skaffman resulted in the following error message:
Hibernate cannot unwrap class org.hsqldb.Session
I did get it to work using SessionImpl rather than Session:
Connection connection = entityManager().unwrap(SessionImpl.class).connection();
An example of solving the problem using Session.doWork() is as follows:
private void executeNative(final String query) {
Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
Statement s = null;
try {
s = connection.createStatement();
s.executeUpdate(query);
}
finally {
if (s != null) {
s.close();
}
}
}
});
}
The JPA API itself doesn't seem to offer this, not surprisingly, but if you're willing to couple your code to a specific implementation, then you can use something like this (Hibernate):
Session hibernateSession = entityManager.unwrap(Session.class);
Connection jdbcConnection = hibernateSession.connection();
Note that Session.connection() is deprecated for removal in Hibernate 4. Consider using Session.doWork() instead.
You must take the underlying delegate using entitymanager.getDelegate() or entitymanager.unwrap(which is the better way), cast it to the specific implementation(in Hibernate it is called Session). Then you can call the connection() method. Be aware this is deprecated, use the Work class instead. Read more here.
In JPA2.0, if need JDBC is por DTO nomodel or entity for query more
complex. Sometimes JPA is not all...
I hope this will help you:
Statement statement = null;
EntityManager em = null;
em = emf.createEntityManager();
EntityTransaction et = em.getTransaction();
if(!et.isActive()) {
et.begin();
}
java.sql.Connection connection = em.unwrap(java.sql.Connection.class);
String qquerry="SELE ...
try {
statement = connection.createStatement();
ResultSet rs = statement.executeQuery(qquerry);
if (!rs.next()) {
return null;
}
else{
wwwwas=rs.getString(4);
}
statement.close();
}
catch (SQLException e) {
System.out.println("\n b-03:"+e);
throw new RuntimeException(e.getMessage(), e);
}
finally {
try {
// em.getTransaction().commit();
if(connection != null )
connection.close();
}
catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
This works awesomely and you can use the connection object elsewhere if needed
SessionImpl sessionImpl = (SessionImpl) session;
Connection conn = sessionImpl.connection();
Where session is the name of the Hibernate Session object
Below is the code that worked for me. We use jpa 1.0, Apache openjpa implementation.
import java.sql.Connection;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAPersistence;
public final class MsSqlDaoFactory {
public static final Connection getConnection(final EntityManager entityManager) {
OpenJPAEntityManager openJPAEntityManager = OpenJPAPersistence.cast(entityManager);
Connection connection = (Connection) openJPAEntityManager.getConnection();
return connection;
}
}

Categories