By using CDI as shown in the next code:
#PersistenceUnit
EntityManagerFactory emf;
I want to inject my hibernate EntityManagerFactory
Currently if I execute the next line:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("HibernatePersistanceProv");
It works just as expected, but if I do it using the first method it tries to use the Derby connection I know this because I get the next error message:
org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection
By the stack trace I know that it is caused because of this.
Error connecting to server localhost on port 1527 with message Connection refused.
Which I know it is because it is trying to connect to the (Java DB) Derby db.
My persistance.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="HibernatePersistanceProv" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/aschema"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value=""/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="false"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/aschema"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
</properties>
</persistence-unit>
</persistence>
I was reading that apparently I need to specify a standalone.xml to provide a different <jta-data-source> but it seams to me a bit more complicated than what it should be (I do not want to chase the wrong rabbit), I have been from the Java EE world for a while so I consider my self as brand new to this (for dummies explanations are widely appreciated).
(If it helps) I am running on a GlassFish 4.1 server. Please ask if any other information is required.
I think your problem is database related but for injecting EntityManager I usually go with this:
public class EntityManagerFactoryProducer {
#Produces
#ApplicationScoped
public EntityManagerFactory create() {
return Persistence.createEntityManagerFactory("HibernatePersistanceProv");
}
public void destroy(#Disposes EntityManagerFactory factory) {
factory.close();
}
}
public class EntityManagerProducer {
#Inject
private EntityManagerFactory emf;
#Produces
#RequestScoped
public EntityManager create() {
return emf.createEntityManager();
}
public void destroy(#Disposes EntityManager em) {
em.close();
}
}
Than just simply inject it where ever you want. If you have more database use qualifier combined with inject.
#Inject
private EntityManager entityManager;
The solution end up being that I was not managing properly my connections pools in Glassfish, in order to achieve this behavior (at least this is the way I found, but I am pretty sure they should be more) you need to:
Glassfish side:
In the "Common Tasks" Panel (left side of the administrator console (Glasfish4) expand JDBC.
Select JDBC Connection Pools and click the New button on top of the main (central) panel, proceed to configure the data base connection for the pool.
Now in the same JDBC section previously mentioned (left panel) select JDBC Resources (should be immediately above JDBC Connection Pools) there you can create a new resource so that you can use the CDI using the name of your choice OR as I did, just configure it in the jdbc/__default connection (as you may imagine that is the default connection provided by Glassfish CDI name space, to select your connection pool click on the link jdbc/__default on the table which appeared on the main (central) panel, that will take you into another form where you could use the drop down labeled as Pool Name: to select your newly configured connection pool, or the one of your choice; save it, top left of the main (central) panel.
Hibernate side:
In the persistence.xml, you can either:
a) Be sure that you are not providing any <jta-data-source> (IFF you configured in the jdbc/__default)
b) Provide the JNDI Name (usually the name you provided for your JDBC Resources (in case you created one) or in the case of the default connection (jdbc/__default) you can see the JDNI name in the edit view (which is: java:comp/DefaultDataSource).
Write that into <jta-data-source> in your persistence.xml and it should do the trick.
Sorry for the lack of graphical resources, I'll try to add them later. I hope it works for you "anonymous reader".
IMPORTANT NOTE I needed to switch back to Glassfish 4 (not 4.1) because Glassfish 4.1 currently (As of Jan `16) has a bug which does not allows you to create new connection pools.
Related
I can persist new data, but I cannot do updates. There are no errors, just no transactions committing the changes. I'm assuming this has something to do with the way that I've set up transactions. I'm trying a bunch of relatively new (to me) set of technologies. Below are the details.
I'm using the following tools/technologies:
Wildfly 8 and Java 7 (which is what my hosting service uses)
Annotations, with minimal XML being the goal
Struts 2.3 (using the convention plugin)
Spring 3.2
Hibernate 4.3
JTA (with container managed transactions (CMT))
JPA 2 (with a Container Managed Persistence Context)
EJBs (I have a remote client app that runs htmlunit tests)
Three WAR files and one EJB JAR file deployed
SpringBeanAutowiringInterceptor to autowire the EJBs (could there be an error in here where transactions don't commit?)
beanRefContext.xml (required by SpringBeanAutowiringInterceptor)
<beans>
<bean
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg value="classpath:campaignerContext.xml" />
</bean>
</beans>
campaignerContext.xml
<beans>
<context:component-scan base-package="..." />
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/CampaignerDS"/>
<tx:annotation-driven/>
<tx:jta-transaction-manager/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="campaigner" />
</bean>
<bean id="ehCacheManager" class="net.sf.ehcache.CacheManager" factory-method="create">
<constructor-arg type="java.net.URL" value="classpath:/campaigner_ehcache.xml"/>
</bean>
</beans>
persistence.xml
<persistence>
<persistence-unit name="campaigner" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:/jdbc/CampaignerDS</jta-data-source>
<class>....UserRegistration</class>
...
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
</properties>
</persistence-unit>
</persistence>
SecurityServiceBean.java
#EnableTransactionManagement
#TransactionManagement(value = TransactionManagementType.CONTAINER)
#TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW)
#Stateless
#Interceptors(SpringBeanAutowiringInterceptor.class)
#DeclareRoles("Security Admin")
public class SecurityServiceBean extends AbstractCampaignerServiceImpl implements
SecurityServiceLocal, SecurityServiceRemote
{
#Override
#PermitAll
#Transactional(propagation = Propagation.REQUIRES_NEW)
public UserRegistration confirmRegistration(
String confirmationCode) throws ApplicationException
{
UserRegistration userRegistration = this.userRegistrationDAO
.find(new UserRegistrationQuery(null, confirmationCode)).uniqueResult(); // Should be attached now
...
userRegistration.setConfirmationDate(new Date());
userRegistration.setState(State.CONFIRMED);
userRegistration = this.userRegistrationDAO.saveOrUpdate(userRegistration);
...
}
}
UserRegistrationDAO.java
#Override
public UserRegistration saveOrUpdate(
UserRegistration obj) throws DAOException
{
log.debug("[saveOrUpdate] isJoinedToTransaction? "
+ (this.em.isJoinedToTransaction() ? "Y " : "N"));
try
{
if (obj.getId() == null)
{
this.em.persist(obj);
log.debug("[saveOrUpdate] called persist()");
return obj;
}
else
{
UserRegistration attached = this.em.merge(obj);
log.debug("[saveOrUpdate] called merge()");
return attached;
}
}
catch (PersistenceException e)
{
throw new DAOException("[saveOrUpdate] obj=" + obj.toString() + ",msg=" + e.getMessage(), e);
}
}
Are there any settings in Wildfly's standalone.xml that you need to see or that I should be setting?
BTW, this is incredibly annoying and frustrating. This should be an easy one-time setup that I can do and then forget about as I move on to creating my website, which should be where most of my time is spent. The lack of comprehensive documentation anywhere is AMAZING. Right now, development has been halted until this is solved
/rant
UPDATES
I tried switching to an XA data source, because some sites claimed that was necessary, but that didn't work (didn't think so but had to try). Also tried configuring emf with dataSource instead of persistenceUnitName as some other sites have. No joy.
I tried replacing the transactionManager with JpaTransactionManager, but that just led to this exception: A JTA EntityManager cannot use getTransaction()
The answer, thanks to M. Deinum, is that I was using the wrong #Transactional. I should have been using javax.transaction.Transactional but was using the Spring one instead. Note that the correct one will look like "#Transactional(TxType.REQUIRES_NEW)" instead of "#Transactional(propagation = Propagation.REQUIRES_NEW)"
I work at a complex web application that use EJB and Hibernate on JBoss. I use singleton EntityManagerFactory, and share it between all running process using Entity Manager instance.
The problem occurs when in a struct action is called an update on entity, and before action ends another process read and update same object.
I have that second process (it's a web service called from third-parts) read an old value and not the updated one in the action.
I know that data become persistent on database only after action end its work and control come back to user. Unfortunately this action after Entity Manager merge execution, it must call a web service that sometimes return after 10s . Meanwhile other process have wrong value if read this object.
I need that merge in first process become instantly persistent, or, I need that other process read right value.
I don't know if the second level cache is working and has effect in this scenario.
A solution is to make an update using JDBC instead Hibernate, but I would like a clean solution to do it.
a brief outline
t0 = start action ;t1= action find and merge entity; t2= start call to web service; t6= web service return; tend = end action ;
t3= start second process ; t4= find and merge entity; t5=end second process
t0 t1 t2 t3 t4 t5 t6 tend
|---------------|--------|------------------------------|-------|
|-----|----|
I need that at t3 the value read is that one merged at t2.
This is my persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="ApplicationWeb_EJB" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/ds/VisiaIntegrazioneDS</jta-data-source>
<class>.....entity.ApplicationServer</class>
....
<class>.....entity.Devices</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect"/>
<!-- Caching properties -->
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<!--<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider" />-->
<property name="net.sf.ehcache.configurationResourceName" value="ehcache.xml"/>
<!--<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>-->
<!--<property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider" />-->
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.cache.region.factory_class" value="net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory"/>
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
<property name="hibernate.max_fetch_depth" value="4"/>
<!-- hibernate.generate_statistics a true produce informazioni su hibernate da loggare -->
<property name="hibernate.generate_statistics" value="true"/>
</properties>
</persistence-unit>
</persistence>
this is an example of Entity Manager update
EntityManager em = EntityMan.getEMF().createEntityManager();
try {
em.find(Devices.class, device.getId());
em.merge(device);
em.flush();
} catch (Exception e) {
logger.debug(e.getMessage());
e.printStackTrace();
}
I am trying to figure out the whole process, so the statements I am reporting below are probably based on a wrong picture.
If I understood correctly, you would have problem even if the web service took 10ms. A request could come in the middle in any case.
But, am I wrong in saying that are you creating the entity manager, rather having injected by the container ? If you share the same manager within all your singleton methods, you can have more control on the cache concurrency access.
Second, if the call to the web service is not mandatory for the final value, is there a particular reason why you are not invoking the web service asynchronously, with a message driven bean or by using #Asynchronous annotation ?
[UPDATE] You cannot have a real time concurrent access application that rely on a 10sec WS response.
For example, if the last step WS call fail, what do you do ? Rollback ? So, what if the new incoming client has read uncommitted data that you later are going to roll back ?
As some other have probably said, make the last WS call asynchronous, or better delegate to a message driven bean (which has the advantage of retry the WS call in case of failure). In either way the call is returned immediately to the client.
But I am quite confident that you will have the same problem again. If I understood the architecture correctly I would revisit the design to do this things:
Invoke the WS asynchronously or with MDB in order to return call to the client
Make calls to the entity manager, at least the one that insist on the same table, to be thread safe. Should be easy, because you have a singleton class
I have a web application running under Tomcat 7 using Spring with c3po as the connection pool manager. I have also used dbcp and have the same result.
I initiate a long running single threaded process which makes a large number of database calls using jdbcTemplate.update(), etc, in various dao's. As each of these updates is simple and independent, no transaction manager is being used.
For some reason, I am running out of connections. What appears to be happening is that each dao is holding onto its own connection and not returning it to the pool.
Is this normal behaviour? I had expected that the connection was tied to the jdbcTemplate.update() and released back as soon as this had finished.
...
In the context file...
<bean id="enquiryDataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${enquiry.drivername}"/>
<property name="url" value="${enquiry.jdbc}"/>
<property name="username" value="${enquiry.username}"/>
<property name="password" value="${enquiry.password}"/>
<property name="maxWait" value="30000"/>
<property name="maxActive" value="50"/>
</bean>
In a typical dao constructor...
#Autowired
public XXXCountryDao(#Qualifier("enquiryDataSource") DataSource dataSource,
#Qualifier("sqlUpdaterFactoryImpl") SqlUpdaterFactory sqlUpdaterFactory, #Qualifier("sqlFormatterFactoryImpl") SqlFormatterFactory sqlFormatterFactory) {
super("Country", dataSource, sqlUpdaterFactory, sqlFormatterFactory);
// ...other constructor stuff
}
All dao's inherit from...
public abstract class AbstractFileProcessorDao<ImportRecType, QueryRecType> extends JdbcDaoSupport {
// ...
}
In a typical dao method...
protected boolean runUpdateToSqlDatabase(Map<String, Object> values, Map<String, Object> whereValues) {
if (values.isEmpty())
return true;
String sql = updateUpdaterServer.getSql(values, whereValues);
if (logger.isDebugEnabled())
logger.debug("Server SQL -> " + sql);
getJdbcTemplate().update(sql);
return false;
}
Please check your application for "rogue" calls to DataSource#getConnection (you can use your IDE to search for method references). Connection leaks are usually caused by obtaining a connection which is then never closed via Connection#close.
When working with Spring's JdbcTemplate all JDBC resource handling (opening / closing connections, statements, result sets) is done automatically. But with legacy code you never know.
I'm trying to implement a entitymanager-per-conversation pattern on a stateful proprietary web framework with JBoss 4.3.0 and Hibernate 4.3.5. In short, the goal is:
First HTTP request loads entity A with lazy-loading properties from the database
In second request, the lazy-loading properties of entity A are accessible without e.g. creating a new EntityManager and calling e.g. entityManager.merge(entityA).
Entitymanager-per-conversation seems like the perfect choice. Here's my attempt:
public class EntityManagerHolder {
private static ThreadLocal<EntityManager> entityManager = new ThreadLocal<EntityManager>();
private static EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myPersistence");
private static ConnectionProvider connectionProvider = new MyConnectionProvider();
public static synchronized EntityManager getEntityManager() {
createEntityManagerIfNeeded();
return entityManager.get();
}
public static synchronized void createEntityManagerIfNeeded() {
if (entityManager.get() == null) {
// Start the conversation
EntityManager newEntityManager = entityManagerFactory.createEntityManager();
entityManager.set(newEntityManager);
newEntityManager.getTransaction().begin();
} else {
// Entitymanager is alive but may have lost its connection
EntityManager existingEntityManager = entityManager.get();
SessionImpl session = existingEntityManager.unwrap(SessionImpl.class);
try {
if (session.connection() == null || session.connection().isClosed()) {
session.reconnect(connectionProvider.getConnection());
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
Persistence.xml:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="myEntityManagerFactory">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<!-- Scan for annotated classes and Hibernate mapping XML files from this JAR -->
<property name="hibernate.archive.autodetection" value="class, hbm" />
<!-- Database connection settings: Use framework connections for database connectivity -->
<property name="hibernate.connection.provider_class" value="foo.bar.MyConnectionProvider"/>
</properties>
</persistence-unit>
</persistence>
When a new HTTP request arrives via the framework, I call EntityManagerHolder.createEntityManagerIfNeeded(). On the second HTTP request, the JDBC connection of the EntityManager has closed and the attempt to revive it via session.reconnect() leads to an exception:
java.lang.IllegalStateException: cannot manually reconnect unless Connection was originally supplied
org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.manualReconnect(LogicalConnectionImpl.java:296)
org.hibernate.internal.SessionImpl.reconnect(SessionImpl.java:478)
I realize I'm probably doing things in a very backwards way, but it would be nice to understand how entitymanager-per-conversation should be implemented. I've found the filter-based Hibernate-specific sample implementation of this pattern, but haven't managed to bend it to my needs yet.
Turns out JBoss was closing the connections. Disabling JBoss from closing JDBC connections would have resolved the issue. However, we wanted to avoid keeping a large number of JDBC connections open for long periods of time.
Best solution found so far is to revive the JDBC connection of the EntityManager, provided that the old connection is closed. I wrote a rough implementation:
EntityManagerFactoryAdapter - Used to reconnect an EntityManager to a new JDBC connection
EntityManagerHolder - Keeps one EntityManager per thread.
At the start of each HTTP request, we invoke EntityManagerHolder.initializeEntityManager(freshJDBCConnectionFromFramework). When the state is removed from the server, we invoke EntityManagerHolder.closeEntityManager(). Persistence.xml no longer has the hibernate.connection.provider_class - we're passing in connections manually.
I'm posting this just in case someone encounters a similar issue. This solution is very unorthodox, I'm hoping to replace it with a better one later.
I'm fairly new to Hibernate and PostgreSQL, but so far it's going well, although I'm running into a problem now that I can't solve. I'm getting an error while filling the database on the very first operation (which is one transaction inserting or updating 1000 rows in the database). The error is:
SQL Error: 0, SQLState: 53300
FATAL: sorry, too many clients already
Exception in thread "main" org.hibernate.exception.GenericJDBCException: Cannot open connection
This is the important code:
#Repository
public class PDBFinderDAO extends GenericDAO<PDBEntry> implements IPDBFinderDAO {
#Override
#Transactional
public void updatePDBEntry(Set<PDBEntry> pdbEntrySet) {
for (PDBEntry pdbEntry : pdbEntrySet) {
getCurrentSession().saveOrUpdate(pdbEntry);
}
}
}
getCurrentSession() is extended from GenericDAO and calls sessionFactory.getCurrentSession().
This is my Hibernate configuration:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<property name="hibernate.connection.url">jdbc:postgresql://localhost/PDBeter</property>
<property name="hibernate.connection.username">xxxx</property>
<property name="hibernate.connection.password">xxxx</property>
<!-- Create or update the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<!-- Use the C3P0 connection pool provider -->
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">300</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.idle_test_period">300</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Batch size -->
<property name="hibernate.jdbc.batch_size">50</property>
<!-- this makes sure the more efficient new id generators are being used,
though these are not backwards compatible with some older databases -->
<property name="hibernate.id.new_generator_mappings">true</property>
<!-- Echo all executed SQL to stdout -->
<!--
<property name="hibernate.show_sql">true</property>
-->
<property name="format_sql">true</property>
<property name="use_sql_comments">true</property>
</session-factory>
</hibernate-configuration>
This is my Spring configuration:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<context:component-scan base-package="nl.ru.cmbi.pdbeter" />
<!-- Transaction Manager -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven />
<!-- Session Factory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation" value="hibernate.cfg.xml" />
<property name="packagesToScan" value="nl.ru.cmbi.pdbeter.core.model.domain" />
</bean>
<!-- Task Executor -->
<task:annotation-driven />
</beans>
I'm not really sure what is going wrong, this should only open one connection every time, and close it afterwards, isn't that what #Transactional is supposed to do? Also, is there an easy way to check how many connections are open at a certain time so that I can check before and after the error how many connections were open?
EDIT: when I check the database nothing has been added, so it can't even make one connection, what is going wrong here?
EDIT: I'm sorry but I already solved it myself. It was a very stupid mistake, there was a very small query using criteria that was executed 1000 times as well, but that one was executed before the transaction, causing it to be executed in 1000 separate transactions/sessions/connections (I think, correct me if I'm wrong!)
EDIT: ok, turns out that didn't solve it at all, cause I needed that small query to see if something was already in the database, and if so, get that object from the database so I could update it's fields/columns/whatever you want to call it.
This is the method in the GenericDAO:
#Override
public PDBEntry findByAccessionCode(String accessionCode) {
return (PDBEntry) createCriteria(Restrictions.eq("accessionCode", accessionCode)).uniqueResult();
}
There is a function that builds the mapped objects that isn't in the DAO, since it converts a raw datafile into the database object, so I wanted to keep that out of database operations and only put the saveOrUpdate() within the database module. The problem I have now is that the findByAccessionCode() is being called a 1000 times during the conversion of the raw datafile to the database objects, because I need to check whether a certain piece of data is already present in the database, and if so, get the object from the database instead of making a new one.
Now how would I execute that query a 1000 times inside one connection in this context? I tried making that conversion method that converts the 1000 files #transactional, but that didn't work.
Here's the conversion method:
private void updatePDBSet(Set<RawPDBEntry> RawPDBEntrySet) {
Set<PDBEntry> pdbEntrySet = new LinkedHashSet<PDBEntry>();
for (RawPDBEntry pdb : RawPDBEntrySet) {
PDBEntry pdbEntry = pdbEntryDAO.findByAccessionCode(pdb.id);
if (pdbEntry == null) {
pdbEntry = new PDBEntry(pdb.id, pdb.header.date);
}
pdbEntry.setHeader(pdb.header.header);
ExpMethod expMethod = new ExpMethod.Builder(pdbEntry, pdb.expMethod.expMethod.toString()).build();
if (pdb.expMethod.resolution != null) {
expMethod.setResolution(pdb.expMethod.resolution);
}
if (pdb.expMethod.rFactor != null) {
expMethod.setRFactor(pdb.expMethod.rFactor.rFactor);
if (pdb.expMethod.rFactor.freeR != null) {
expMethod.setFreeR(pdb.expMethod.rFactor.freeR);
}
}
if (pdb.hetGroups != null) {
for (PFHetId hetId : pdb.hetGroups.hetIdList) {
HetGroup hetGroup = new HetGroup(pdbEntry, hetId.hetId);
if (hetId.nAtom != null) {
hetGroup.setNAtom(hetId.nAtom);
}
if (hetId.name != null) {
hetGroup.setName(hetId.name);
}
}
}
for (PFChain chain : pdb.chainList) {
new Chain(pdbEntry, chain.chain);
}
pdbEntrySet.add(pdbEntry);
}
pdbFinderDAO.updatePDBEntry(pdbEntrySet);
}
(The pdbFinderDAO.updatePDBEntry(pdbEntrySet) was where I originally thought the problem originated)
EDIT: First of all sorry that I created this new post, I really thought I found the answer, but I'll just continue in this post for further edits.
Ok, now I put all the 1000 findAccessionCode criteria inside the DAO by sending a Set of the raw data files to the DAO so it can retrieve the id's there, then finding them in the database, getting the ones it can find and adding it to a HashMap where the database object is mapped with the reference to the raw data file as key (so I know what raw data belongs to what database entry). This function I made #Transactional like so:
#Override
#Transactional
public Map<RawPDBEntry, PDBEntry> getRawPDBEntryToPDBEntryMap(Set<RawPDBEntry> rawPDBEntrySet) {
Map<RawPDBEntry, PDBEntry> RawPDBEntryToPDBEntryMap = new HashMap<RawPDBEntry, PDBEntry>();
for (RawPDBEntry pdb : rawPDBEntrySet) {
RawPDBEntryToPDBEntryMap.put(pdb, (PDBEntry) createCriteria(Restrictions.eq("accessionCode", pdb.id)).uniqueResult());
}
return RawPDBEntryToPDBEntryMap;
}
Still, no success... I get the exact same error, but it does tell me it's the criteria that causes it. Why can't I execute all these 1000 queries within the same connection?
EDIT: Yet another update: I tried adding all the queries 1 by 1, and this worked, slowly, but it worked. I did this on an empty database. Next I tried the same thing but now the database already contained the stuff from the first try, and I got the following error:
Exception in thread "main" org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
I'm guessing this has something to do with the fact that I'm getting the objects (that are already in the database and so have to be updated) within the DAO, then sending references back to the conversion method, then changing their fields, then sending them back to the DAO to make them persistent. Although after googling a bit I found people that had problems with collections in their POJO's with the annotation:
#OneToMany(mappedBy = "pdbEntry", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
where the cascading caused the problem. Do I have to remove all of these cascades and hardcode the saveOrUpdate() operations for all the different mapped objects? Or has this nothing to do with that error?
And finally: I'm still no closer to figuring out how to do this for 1000 objects at a time.
Solved the problem, it had to do with a bad setup of Spring, which caused the #Transactional to not be recognized. I fixed that, and then the error went away.