My hibernate config:
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
properties.put("hibernate.hbm2ddl.auto", "validate");
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.id.new_generator_mappings", "false");
properties.put("hibernate.connection.autocommit", "true");
properties.put("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
properties.put("hibernate.connection.url", DBConnection.url);
properties.put("hibernate.connection.username", DBConnection.username);
properties.put("hibernate.connection.password", DBConnection.password);
Code example:
// pattern 1
Session s = sessionFactory.openSession();
ObjectA A = s.load(ObjectA.class, pk);
A.setAttr("abc");
s.update(A);
s.close();
// pattern 2
Session s = sessionFactory.openSession();
s.beginTransaction();
ObjectA A = s.load(ObjectA.class, pk);
A.setAttr("abc");
s.update(A);
s.close();
// pattern 3
Session s = sessionFactory.openSession();
Transaction tx = s.beginTransaction();
ObjectA A = s.load(ObjectA.class, pk);
A.setAttr("abc");
s.update(A);
tx.commit();
s.close();
Please ignore my compilation error. I am using hibernate in web application (without spring), and without using transaction, because I am using MySql database and MySql autocommit is true, so in turn, in hibernate, I make it as autocommit true as well. (I am using mysql-connector-java-5.1.23-bin.jar too).
Three of the pattern, I am only able to get pattern 3 works. I am totally confused now. I have few questions below:
1) I can't understand why pattern 1 is not working, all my select (via hibernate CriteriaBuilder or load) and insert (via hibernate session.save) works but only update doesn't work.
2) OK then I try using transaction like pattern 2, my hibernate auto-commit is true, so I assume when I close the session, the transaction should auto-commit but it doesn't work. Why?
3) Pattern 3 works, why I need transaction manager here? I want the jdbc to execute each single query in each transaction (one sql in one transaction), I don't worry the performance, but I have to include transaction here, why?
For pattern 1 and 2, I found that the update script is not even generated (based on hibernate log), the problem is not because script is generated but commit failed. Don't understand why? Please help...
PS:
Just wrap up some points for future reference after some trial and error:
1) Hibernate will only generate sql script upon the session.flush() is called but not tx.commit(), and session.flush() have to be called in Transaction block. without Transaction, it leads to exception. Explicit flush is not needed if the flush mode is auto, commit() will trigger flush.
2) Hibernate Transaction is not equivalent to database transaction, after some tries, I found that, if hibernate autocommit is false, yes, they are functionally equivalent and corresponding begin transaction script is generated via JDBC and send over to database (my guess only). If hibernate autocommit is true, no begin transaction is started although we declare it in hibernate Transaction tx = s.beginTransaction(), all the query will be autocommit and rollback will not work.
3) The reason of my case, session.save() (and also select) work without Transaction, it is a bit special because save have to be triggered in order to get the table identifier(primary key) and so sql script generated even without flush.
4) For pattern 2, I miss-understood, autocommit doesn't mean autocommit upon session closed, its true meaning should be autocommit upon each sql reach database. so pattern 2 will not work because there is no tx.commit, meaning there is no flush, so no sql script is generated. (whether tx.commit will be called automatically upon session.close, it depend on vendor implementation, some will be rollback.)
Conclusion, Transaction block is needed in Hibernate not matter what.
I think you have a bit of confusion. The transaction (org.hibernate.transaction) is not exactly a DB transaction.
Such Object are used by hibernate when you flush the Session (Session.flush) to bound the instruction in a single db transaction. In other word do not confuse Hibernate Session with DB session, nevertheless do not confue hibernate Sessio with db connection.
Most important is that by specificatio hibernate generate sql code only for what is included between a hibernate transaction. That's why pattern A and B doesn't work and doesn't generate sql code. More specifically the auto-commit in pattern B has no influence since the sql cod is never generated. Moreover, according with hibernate best pracitces, you have to remember to open and close a transaction even for simple select instruction. By the way a select should work even without transaction, but you may have some trouble.
To better understand the concept we can resume the architecture:
hibernate session: is a container, wich hold your hibernate object and your db operations as java objects, and many other things.
the hibernate transaction: is a transaction object referred to an hibernate session.
db connection: is your connection to DB
conenction pool: is a set ofdb connection.
What appen when a session is flushed can be resumed with the followoing step:
a connection is get from the connection pool
for each committed
transaction in your session a db connection is get from the pool,
the sql commands are generated and sent to DB
the db connection is put back on the pool
it is just a small recap, but hope this help
r.
Related
I am quite new to hibernate and I was learning about first-level caching in hibernate. I have a concern in first level cache consistency.
Imagine I have two separate Web Applications which can read/write to the same database. Both applications use hibernate. First application consists following code segment.
//First Application code
//Open the hibernate session
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
//fetch the client entity from database first time
Client client= (Client) session.load(Client.class, new Integer(77869));
System.out.println(client.getName());
//execute some code which runs for several minutes
//fetch the client entity again
client= (Client) session.load(Client.class, new Integer(77869));
System.out.println(client.getName());
session.getTransaction().commit();
The second application consists of the following code.
//Second Application code
//Open the hibernate session
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
//fetch the client entity from database first time
String hql = "UPDATE client set name = :name WHERE id = :client_id";
Query query = session.createQuery(hql);
query.setParameter("name", "Kumar");
query.setParameter("client_id", "77869");
int result = query.executeUpdate();
System.out.println("Rows affected: " + result);
session.getTransaction().commit();
Let's say the first application creates the session at 10.00 AM. the first application keeps that session object live for 10 minutes. Meanwhile, at 10.01AM second application do an update to Database (update CLIENT set name = 'Kumar' where id = 77869).
So first level cache in first application is outdated after 10.01AM. am I right? if so, is there any method to avoid this scenario?
There is no implicit way your first application will know about the underlying changes that were triggered by the second application.
One of the possible ways to handle this situation could be the following:
1) Once one of the applications does update / insert it needs to save a flag of some sort in the database for example that the data has been changed.
2) In the other application you just after you start a new session you need to check whether that flag is set and therefore the data has been altered.
If so, you need to set the CacheMode of the session accordingly:
session.setCacheMode(CacheMode.REFRESH);
This would ensure that during this session all the queried entities will not be taken from the cache but from the DB (therefore updating the cache in the process). Most likely this will not update all the changes in the second-level change, you would need to set that session attribute periodically every now and then.
Keep in mind that second-level-caching anything else than dictionary entities that do not really change is tricky in terms of consistency.
I'm running some load tests on our system and I notice a massive number of "SET autocommit=0" and "SET autocommit=1" queries being executed. Something around 25,000 within 1 minute. I'm trying to figure out what is causing this and how to get rid of it.
We use the following technologies:
MySQL
Hibernate
Hikari
Spring
Tomcat
I have tried the following but it did not seem to help:
"SET autocommit = 0" in MySQL
Added the elideSetAutoCommits property in the db connection URL. "jdbc:mysql://localhost/db_name?useUniCode=true&characterEncoding=UTF-8&pinGlobalTxToPhysicalConnection=true&elideSetAutoCommits=true"
Could someone point me towards what might be causing these queries?
Could someone point me towards what might be causing these queries?
Your queries are the consequence of Connection#setAutoCommit(boolean) which is used to switch from the default mode that is auto-commit mode to transactional mode in order to insert/update/delete/read data within a transaction.
The common code is :
// Switch to transactional mode which actually triggers a SET autocommit = 0
con.setAutoCommit(false);
try {
// Some operations on the db
con.commit();
} finally {
// Switch back to auto-commit mode which actually triggers a SET autocommit = 1
con.setAutoCommit(true);
}
Here is a good link that explains how transactions work in JDBC.
If you know that your pool of connections will always be used to get connections in transactional mode, you can set the default mode in the configuration of Hikari thanks to the parameter autoCommit to set to false this way the connections will be already in transactional mode such that it won't be needed anymore to modify the mode.
This property controls the default auto-commit behavior of connections
returned from the pool. It is a boolean value. Default: true
More details about the configuration of Hikari here.
I have the following case:
openSession()
tx = session.beginTransaction();
try
{
...
session.saveOrUpdate(obj_1);
...
session.saveOrUpdate(obj_2);
...
session.saveOrUpdate(obj_3);
session.flush();
tx.commit();
}
catch()
{
tx.rollback()
}
finally
{
session.close();
}
The first call of saveOrUpdate(obj_1) will failed due to Duplicate entry error. But this error would not actually happen until the database is accessed at the end of the session.flush().
Is there a way to make this kind of error happens early and give me more chance to correctly handle it?
I also don't want to cut the transaction too small because it is hard to rollback.
My Hibernate entities are reversed from an existing database schema.
I have disabled the cache in my configuration file:
<property name="hibernate.cache.use_second_level_cache">false</property>
<property name="hibernate.cache.use_query_cache">false</property>
EDIT:
My question is how can I get the error as early as possible if any. The error itself doesn't matter to me.
Right now the error happens at the end, but it actually happened at the first access.
You can execute session.flush() after each session.saveOrUpdate to force hibernate to execute the update or insert on your database. Without the flush command the actual database operations are deferred and will only be processed immediately before ending the transaction.
This way you don't have to change the size of your transaction.
You can execute Surendran after each session.saveOrUpdate to force hibernate to execute the update or insert on your database. Without the flush command the actual database operations are deferred and will only be processed immediately before ending the transaction.
This way you don't have to change the size of your transaction.
We are having two instances of oracle (two physical machines) and one schema for our application.
we are using weblogic application server. application uses datasource for which XA transaction is enabled.
I am having one bean managed EJB, where i do -
update some data in table and then commit
submit oracle job
again update some data in table and then commit
Here i am getting error - java.sql.SQLException: could not use local transaction commit in a global transaction.
strangely this error is not commig every execution, it is comming 1 in 7-8 executions.
Now my questions are
What is significance of bean managed transaction if i use XA enabled transaction ?
why it is not encountering in every execution ?
Thanks.
below is code -
DataObject.updateDataAndReturnCount(" UPDATE EOD_Trn_BatchProcess SET iJobNo = ?, szParameters = ? WHERE iProcessSeqNo = ? ", conn, new String[]{null, strParameters, (String)mapParameters.get("__PROCESS_SEQ_NO")});
conn.commit();
String strStatement = "{? = call submitProcAsJob(?, ?)}";
//String strStatement = "begin ? := submitProcAsJob(?, ?); end;";
CallableStatement pStmt = conn.prepareCall(strStatement);
pStmt.registerOutParameter(1, OracleTypes.NUMBER);
pStmt.setObject(2, strJobName);
pStmt.setObject(3, strInstanceNo);
pStmt.execute();
vString strJobNo = pStmt.getString(1);
vpStmt.close();
DataObject.updateData(" UPDATE EOD_Trn_BatchProcess SET iJobNo = ?, szParameters = ? WHERE iProcessSeqNo = ? ", conn, new String[]{strJobNo, strParameters, (String)mapParameters.get("__PROCESS_SEQ_NO")});
conn.commit();
here first commit is required because i want to save parameters used during call, even if job submission fails.(or any thing ahead.)
The reason for the exception is that you can not mannaully call commit()/rollback under a global transaction,you can only marked it for rollback.You have three options:
Throw an exception,which depends on the ejb-jar.xml/weblogic-ejb-jar.xml,the default is for any RuntimeException the transaction is marked for rollback;
call the EJBContext.setRollbackOnly() method in case of CheckedException or whenever you need;
If none of above happend for all the resouces under the same transaction,it will be commited sooner or later by the transaction manager.
The transaction manager is responsible for commit()/rollback() the transaction for you, so that the it has a chance to co-operate with
different resources(two oralce db for example).You can check the detail by gooble the key word "two phased transaction" or "global transaction",here is what I found:
Global Transaction
As for your question
What is significance of bean managed transaction if i use XA enabled transaction ?
The bean-managed transaction is a "Global transaction" if the transaction-attribute in in ejb-jar.xml enable the transaction propagation. A global transaction need the datasource to be XA enabled,that is the jdbc driver itself is XA kind driver such as oracle.jdbc.xa.client.OracleXADataSource ,or the thin driver oracle.jdbc.OracleDriver with XA enabled (a simulation of two phased transaction,but not real one )
why it is not encountering in every execution ?
I am not sure why,I guess the driver have some mechanism to check whether the ruled is breaked.Or the transaction-attribute is configured to Supports ,so if caller has a transaction context,then your ejb is under global transaction,otherwise not.
I wish my answer to be helpful,good luck!
I've encountered the same problem,and when I set local transaction to auto commit false solved the problem.
Connection.setAutoCommit(false)
I guess you are using a 2-phrase transaction,What if you do not commit in you 1st step?
I want to write a transaction using jdbc in java.
I have tried this simple transaction
"BEGIN TRANSACTION"+NL+"GO"+NL+"UPDATE table SET col='test' where id=1010"+NL+"GO"+NL+"COMMIT"
I have tried with
NL= "\n" and NL="\r\n" and NL="\r"
but I get always the following error:
java.sql.SQLException: Incorrect syntax near 'GO'.
In sql server management studio the transaction works
Get your Connection object. Turn off auto commit.
connection.setAutoCommit(false);
Wrap your entire transaction in a try-catch block. When you finish processing your inserts/updates, call:
connection.commit();
If you get an exception, call:
connection.rollback();
Don't put the transaction statements in your JDBC's SQL at that point. I suggest looking at wrappers, such as Hibernate and JPA. Transactions in JDBC can get pretty long winded.