I have a propertiesbean which gives me SqlSessions.
This bean is annotated with all this jazz # the class level
#Singleton
#LocalBean
#Startup
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
i also have this
public SqlSessionFactory getSqlSessionFactory(ConnTypes connType) {...}
which returns the SqlSession you want.
I have two databases. One is a mysql instance and the other one isn't (lets call it db2).
When I run the project locally everything is fine. Both databases are accessed with no problem.
When I run the project on our test server it starts to throw a Client Transaction aborted error. I've done a fair amount of research on this and it seems like people get these exceptions when there is a problem with a database query or some database access. The entire transaction is then marked as rollback and this exception is thrown (at least that's what i've read)
It looks to me like it throws an XA-Resource error right before the first time it throws the client's transaction aborted error. I know that you can get those when your bean tries to access another session. I've seen this error before when running locally and trying to maintain connections to both databases in one method.
Could it be that my singleton properties bean, which accesses both databases, gets into a weird transactional state trying to return a mysql session in one thread and a db2 session in the other?
Related
I've got a couple of class instance variables: userSF which is a SessionFactory and userCfg which is a Configuration set up to use user.cfg.xml. When I have someone log into my application their database login information is grabbed from a file the following happens:
userCfg.setProperty("hibernate.connection.username", username);
userCfg.setProperty("hibernate.connection.password", password);
userSF = userCfg.buildSessionFactory();
When the user logs out of the application it closes the session factory. then when another user tries to log in that same code is called with username and password changing based on who is logging in. The problem is that the second time it's called I'm getting an exception:
org.hibernate.HibernateException: HHH000469: The ClassLoaderService can not be reused. This instance was stopped already.
Is there a way to reuse the SessionFactory variable for multiple logins?
You should not create a SessionFactory for each logged in user / configuration. It is a very costly component and should target the Singleton pattern. Use a single instance per context within your application.
If you have multiple configurations you can cache multiple instances in application scope. Depending on your application this maybe a static Singleton class using a Map<MyUserPasswordKey, SessionFactory> or in an JAVA-EE container a #ApplicationScoped EJB or JSF-Bean.
In case a context based on a user-password key will lead to too many SessionFactory instances you maybe should change your application to either manage them in permission groups or restrict your functionality within your data access or service layer.
In a CMT J2EE environement (Container Managed Transaction) what transaction / connection is used when I JDNI-lookup for a DataSource object and invoke DataSource.getConnection?
Is this connection part of the (potentially distributed) transaction? Does getConnection() return the same Connection every time I call it for the same DataSource object? I only know using Connections by the same EntityManager using native SQL statements.
It is something that keeps me puzzling. As I understood the SessionContext defines a transactional system that is used throught every time I use a datasource. I have the problem that inside a session bean a DataSource.getConnection() is used and this connection is then closed. If a problem is encountered SessionContext.setForRollBack(true) is issued.
Therefore how does the transactional context of a service relate to a DataSource?
If we spawn a new Connection each time a datasource is used or at least looked up, I have problems to understand things I already know. Any clarification would be wonderful. I know container managed transaction and other systems, but the actual behavior of DataSource is totally beyond me.
In Java EE a transaction is a concept that is not exclusive to databases, for example JMS connection sessions can also be part of container managed transaction. The idea is if one or more than one method is running under container managed transaction boundary, the container will commit or rollback the transaction as needed.
In data base related data source, there are multiple layers, fist is the managed pool of connection which is maintained by the container, second is the actual connection management of the database driver with the database, in Java a Connection is an abstraction for a session with a database and not a physical connection, that is managed by the driver.
With the above context, your questions could be addressed viz.:
what transaction / connection is used when I JDNI-lookup for a DataSource object and invoke DataSource.getConnection.
Under container managed transaction, though it is implementation dependent, a connection/session with the database is associated that is marked with the transaction boundary. The actual physical connection could be shared with the database by the driver but that is transparent to application as well as the container.
Is this connection part of the (potentially distributed) transaction? Does getConnection return the same Connection every time I call it for the same DataSource object?
Refer above, a Connection has no relation with the underlying socket to the database as opened by the driver. It is logically a separate session and if within transaction boundary, the same session is associated with the connection retrieved from the data source, how this is implemented is part of the container design
I have the problem that inside a session bean a DataSource.getConnection is used and this connection is then closed
In a pooled implementation for connection a Connection.close() has no impact, the connection is returned to the pool(http://commons.apache.org/proper/commons-dbcp/api-1.3/org/apache/commons/dbcp/PoolableConnection.html), this behaviour is similar for all pools. So clsing a connection does not necessarily disassociate it from container transaction boundary, though a connection should not be closed within container managed transaction. Similarly, commit, setAutoCommit, rollback must not be called from within CMT as this will issue the following command equivalent to the actual database and the behaviour of the transaction after that will undefined.
Have a look at the declaration of the #Resource annotation.
It includes a shareable attribute that allows you to specify connection sharing behaviour. It defaults to true, which means that you will get connection sharing automatically if you do nothing.
This attribute is also included in the XML schema for any resource-ref defined datasources that you may lookup using JNDI.
All resources (JDBC, JMS, ResourceManager) that are included during an invocation are enrolled in the current transaction. You may sometimes need to specify the use of XA for this to work correctly.
In webspehere App Server(WAS)version 8,getting the below error with Non-XA datasource.I have changed to XA datasource to test but giving different error as connection timeout/not available.Below is the error for Non-XA Datasource:
RegisteredRes E WTRN0062E: An illegal attempt to use multiple resources that have only one-phase capability has occurred within a global transaction.
LocalTransact E J2CA0030E: Method enlist caught com.ibm.ws.Transaction.IllegalResourceIn2PCTransactionException: Illegal attempt to enlist multiple 1PC XAResources
at com.ibm.ws.tx.jta.RegisteredResources.enlistResource(RegisteredResources.java:870)
Interesting part is it is working fine in WAS6.Really appreciate if anyone can suggest something?
This error is indicating that you are using at least two transactional resources (databases,queues, SAP managed connections,... ) inside a global transaction.
Within a globlal transaction, all resources must support Two Phase Commits (or at least, all except one, if last participant support is enabled)
If you are using QueueConnectionFactories, there are a checkbox to enable XA. Regarding datasources, you should use the XA driver and so on.
I would double check all the resources to assure all of them are configured to support 2PC.
Regards
I had a web software running in a Jboss AS 7 container witch saves our data in a PostgreSQL 9.1 database via JPA, an its configuration delegated to JTA.
Last year it was adapted to run at AWS EC2 cloud. As the user demand grown our database usage growed too. As expected our database server becomes busy at rush times, an it affected the usage experience from our users.
After some replication researches on PostgreSQL we realise that PGPool2 could be a nice replication solution for our case: it offers Load Balancing for SELECT queries, and Replication for CUD operations ( UPDATE, INSERT and DELETE ) as well.
So far so good, except that it turns the software slow. If fact, as explicited in PGPool2 documentation, SELECT queries will not be load balanced if it was defined in explicit BEGIN/END transaction.
For a query to be load balanced, all the following requirements must be met:
- PostgreSQL version 7.4 or later
- the query must not be in an explicitly declared transaction (i.e. not in a BEGIN ~ END block)
- it's not SELECT nextval or SELECT setval
- it's not SELECT INTO
- it's not SELECT FOR UPDATE nor FOR SHARE
- it starts with "SELECT" or one of COPY TO STDOUT, EXPLAIN, EXPLAIN ANALYZE SELECT...
- ignore_leading_white_space = true will ignore leading white space.
Two questions:
How I could figure out our SELECT queries that was running in explicit transactions?
Does _javax.ejb.TransactionAttributeType.NOT_SUPPORTED_ fix the transaction scopes, granting that my SELECT method will be running as "transaction-free"?
How I could figure out our SELECT queries that was running in explicit transactions?
Turn on pgpool2 logging of SQL and connections:
Put the following statements into pgpool.conf (which you can setup via cp $prefix/etc/pgpool.conf.sample $prefix/etc/pgpool.conf):
log_per_node_statement
log_connections
Alternatively, turn on log tracing of JPA:
This requires a different method depending or your JPA implementation ( How to view the SQL queries issued by JPA? , JPA 2.0 (logging and tracing through) with Glassfish 3.0.1 and NetBeans 6.9.1: ).
This will log SQL, but will not log transaction start/commit/rollback.
Additionally, put your own debug logging code into methods which start & end transactions, so that you can see when transaction start/commit/rollback.
Does _javax.ejb.TransactionAttributeType.NOT_SUPPORTED_ fix the transaction scopes, granting that my SELECT method will be running as "transaction-free"?
If you are using Container Managed Transactions (annotations #TransactionManagement(CONTAINER) and #TransactionAttribute), then NOT_SUPPORTED will temporarily disassocate the JTA transaction from the current thread. Then the method will run with no transaction context.
Your subsequent JPA query will run outside of the JTA transaction - because the JTA transaction is not available for it to use.
If you already use a Transaction-Scoped EntityManager
Within your Stateless Session Bean you have an EntityManager annotated
#PersistenceContext(type=PersistenceContextType.TRANSACTION), or
annotated #PersistenceContext without type attribute (because
TRANSACTION is the default):
then that EM will lose it's persistence context within the NOT_SUPPORTED method because the PC is associated with the current transaction, which is no longer accessible
so you cannot use such an EM in the method (e.g. to run queries or lookup cached objects)
so you must use an additional application-managed EM within the NOT_SUPPORTED method
you must create the app-managed EM from an EntityManagerFactory in a place where no JTA transaction is active (e.g. in the NOT_SUPPORTED method), because the app-managed EM will automatically associate itself with the current thread's JTA transaction during creation
any objects returned from queries by the new app-managed EM will be in a different persistence context from the original EM, so you need great care to cleanly detach such objects from the PC (e.g. appMgdEM.clear() or appMgdEM.close() or appMgdEM.detach(someEntity)) if you are to modify/merge them with the original EM.
If you already use an Extended-Scoped EntityManager
Within your Stateful Session Bean you have an EntityManager annotated #PersistenceContext(type=PersistenceContextType.EXTENDED).
then that EM will still have it's persistence context within the NOT_SUPPORTED method because the PC is associated with the stateful session bean
but the EM is using a connection that is already in the middle of a "live" transaction
so if you want to run queries outside of a transaction, you cannot use such an EM in the method
so again, you must use an additional application-managed EM within the NOT_SUPPORTED method (same points apply as above).
Example
#Stateless
public class DepartmentManagerBean implements DepartmentManager {
#PersistenceUnit(unitName="EmployeeService")
EntityManager txScopedEM;
#PersistenceUnit(unitName="EmployeeService")
EntityManagerFactory emf;
#TranactionAttribute(REQUIRED)
public void modifyDepartment(int deptId) {
Department dept = txScopedEM.find(Department.class, deptId);
dept.setName("New Dept Name");
List<Employee> empList = getEmpList();
for(Employee emp : empList) {
txScopedEM.merge(emp);
dept.addEmployee(emp);
}
dept.setEmployeeCount(empList.size());
}
#TranactionAttribute(NOT_SUPPORTED)
public void getEmpList() {
EntityManager appManagedEM = emf.createEntityManager();
TypedQuery<Employee> empQuery = appManagedEM.createQuery("...", Employee.class);
List<Employee> empList = empQuery.getResultList();
// ...
appManagedEM.clear();
return empList;
}
}
Alternative/Adjusted Approach
The above has some restrictions on how you query and how you use resulting objects. It requires creating an EM "on the fly", if you use stateless session beans, and also requires entityManager.merge() to be called. It may not suit you.
A strong alternative is to redesign your application, so that you run all queries before the transaction starts. Then it should be possible to use a single Extended-Scoped EntityManager. Run the queries in "NOT_SUPPORTED" method 1 (no transaction), using extended-scope EM. Then run modifications in "REQUIRED" method 2 (with transaction), using the same extended-scope EM. A Transaction-Scoped EntityManaged wouldn't work (it would try to be transactional from the very start, and would have no PC in NOT_SUPPORTED methods).
Cheers :)
You may want to consider partitioning in JPA using EclipseLink data partitioning,
http://java-persistence-performance.blogspot.com/2011/05/data-partitioning-scaling-database.html
I have a Spring 3 application using openJPA as persistence management, following section works fine in STS/Tomcat
#Transactional
createBalance(){
.....
Balance balance = new SummaryBalance();
balance.setName(name);
balance.setCurrency(currency);
balance.setClosingTimestamp(closingTime);
balance.setStatus(BalanceStatus.OPEN);
balance.persist(); // persist !!
......
balance.setCloseAmount(amount);
balance.setLastUpdateTimestamp(now);
}
However, when deploying same code in websphere 7, the closeAmount and lastUpdate does not update(both fields in DB didn't get update but from log both field can return values by their getter) then show up as null, but changes to other fields before persist() do take effect when the method finished. So I bet when the method finishing WS didn't flush the changes towards these fields.
I thought the JPA(regardless of vendor) should keep the balance entity object managed after persist() and flush the object after the method is finished with later changes. Turns out Websphere 7 doesn't make it. Even I put a merge() method
balance.setCloseAmount(amount);
balance.setLastUpdateTimestamp(now);
balance.merge();
still does not help.
Questions:
OpenJPA has already been included as dependencies in the deployment, but why still websphere need to involve with the JPA management?
How to solve the problem?
Thanks in advance.
I'm not sure that this exactly answers your question, but I think you should do some reconfiguration to use WebSphere capabilities, please check Spring 3.1 documentation
11.8.1 IBM WebSphere
On WebSphere 6.1.0.9 and above, the recommended Spring JTA transaction
manager to use is WebSphereUowTransactionManager. This special adapter
leverages IBM's UOWManager API, which is available in WebSphere
Application Server 6.0.2.19 and later and 6.1.0.9 and later. With this
adapter, Spring-driven transaction suspension (suspend/resume as
initiated by PROPAGATION_REQUIRES_NEW) is officially supported by IBM!
and
11.9.1 Use of the wrong transaction manager for a specific DataSource
Use the correct PlatformTransactionManager implementation based on
your choice of transactional technologies and requirements. Used
properly, the Spring Framework merely provides a straightforward and
portable abstraction. If you are using global transactions, you must
use the org.springframework.transaction.jta.JtaTransactionManager
class (or an application server-specific subclass of it) for all your
transactional operations. Otherwise the transaction infrastructure
attempts to perform local transactions on resources such as container
DataSource instances. Such local transactions do not make sense, and a
good application server treats them as errors.
Figure out a solution myself with guess work. Simply just place the persist() by the end of the whole method body.
#Transactional
createBalance(){
.....
Balance balance = new SummaryBalance();
balance.setName(name);
balance.setCurrency(currency);
balance.setClosingTimestamp(closingTime);
balance.setStatus(BalanceStatus.OPEN);
......
balance.setCloseAmount(amount);
balance.setLastUpdateTimestamp(now);
......
balance.persist(); // persist !!
}
That can make sure every fields are set before the method finished.
Both merge() and explicit flush() won't do the job but only with above compromise. Still not quite sure about the official work around....
I will keep this thread open for any new thinking come in :)