I would like to fetch multiple Hibernate mapped objects from a database in a batch. As far as I know this is not currently supported by Hibernate (or any Java ORM I know of). So I wrote a driver using RMI that implements this API:
interface HibernateBatchDriver extends Remote
{
Serializable [] execute (String [] hqlQueries) throws RemoteException;
}
The implementation of this API opens a Hibernate session against the local database, issues the queries one by one, batches up the results, and returns them to the caller. The problem with this is that the fetched objects no longer have any Session attached to them after being sent back, and as a result accessing lazily-fetched fields from such objects later on ends up with a no session error. Is there a solution to this problem? I don't think Session objects are serializable otherwise I would have sent them over the wire as well.
As #dcernahoschi mentioned, Session object is Serializable, but the JDBC connection is not. Serializable means that you save something to a file, later you read it and it's the same object. You can't save a JDBC connection to a file, and restore it later from that file. You should have to open a new JDBC connection.
So, even though you could send the session via RMI, you would need JDBC connection in the remote computer as well. But if it was possible to setup a session in the remote computer, then why not execute the queries in that computer?
If you want to send the query results via RMI, then you need to do is fetch the whole objects without lazily fetching. In order to do that you must define all relationships as eagerly fetched in your mappings.
If you can't change the mappings to eager, then there is an alternative to get a "deep" copy of each object and send this object through RMI. Creating a deep copy of your objects will take some effort, but if you can't change the mapping to eager fetching it is the only solution.
This approach means that your interface method must change to something like:
List[] execute (String [] hqlQueries) throws RemoteException;
Each list in the method result will keep the results fetched by one query.
Hibernate Session objects are Serializable. The underlying JDBC connection is not. So you can disconnect() the session from the JDBC connection before serialization and reconnect() it after deserialization.
Unfortunately this won't help you very much if you need to send the session to a host where you can't obtain a new JDBC connection. So the only option is to fully load the objects, serialize and send them to the remote host.
Related
In my service layer, I am calling a dao class method which is calling
session().get(xyz.class, id);
I am returning the retrieved object back to service class. Then I am updating this object and passing the updated object to dao class update method.
getSession().saveOrUpdate(xyzObj);
But here instead of an update I am getting this exception
org.hibernate.HibernateException: illegally attempted to associate a proxy with two open Sessions
Please tell me the right way to do a select an update of the same object.
Found the problem.
I was using
sessionFactory.openSession();
to get the session object. So multiple sessions were getting created. Replaced it with
sessionFactory.getCurrentSession();
Now its working fine.
Try closing the first session before getting another one, or use the same session for both operations. As an alternative i think: getSession().merge(xyzObj); should work.
Place all DA operation with the same entity (or entities) into one Service (or named DAO) class, which encapsulate work with Session.
I have to implement a Neo4j Server Plugin that reacts to changes to the Database and get's information about those changes. I need to get all the Data that has been added, changed and deleted in a transaction. I use a TransactionEventHandler registed to the database. For performance reasons i have to use the afterCommit callback that is called after the changes to the database have been made. This way the transaction will not be held back by the plugin.
Now inside this callback i do something similiar to this:
public void afterCommit(TransactionData data, Void arg1) {
for(Node n:data.createdNodes()) {
String firstkey = n.getPropertyKeys().iterator().next();
}
}
But the getPropertyKeys throws an Exception because the transaction has already been commited. I don't understand why this is a problem, i don't want to change anything to the transaction, i just want properties the node has that has been changed. Is there some way to work around this? What is the reason for the Exception?
The Exception:
java.lang.IllegalStateException: This transaction has already been completed.
at org.neo4j.kernel.impl.api.KernelTransactionImplementation.assertTransactionOpen(KernelTransactionImplementation.java:376)
at org.neo4j.kernel.impl.api.KernelTransactionImplementation.acquireStatement(KernelTransactionImplementation.java:261)
at org.neo4j.kernel.impl.api.KernelTransactionImplementation.acquireStatement(KernelTransactionImplementation.java:80)
at org.neo4j.kernel.impl.core.ThreadToStatementContextBridge.instance(ThreadToStatementContextBridge.java:64)
at org.neo4j.kernel.InternalAbstractGraphDatabase$8.statement(InternalAbstractGraphDatabase.java:785)
at org.neo4j.kernel.impl.core.NodeProxy.getPropertyKeys(NodeProxy.java:358)
at de.example.neo4jVersionControl.ChangeEventListener.afterCommit(ChangeEventListener.java:41)
In afterCommit the transaction has already been committed (hence the name). To access properties from a node you need a transactional context - remember that every operations (even readonly) require this.
The recommended way for implementations of TransactionEventHandlers is to rely on TransactionData only. TransactionData.assignedNodeProperties() will return the properties of the newly created nodes as well.
I'm using Spring and JDBC template to manage database access, but build the actual SQL queries using JOOQ. For instance, one DAO may look like the following:
public List<DrupalTaxonomyLocationTerm> getLocations(String value, String language) throws DataAccessException {
DSLContext ctx = DSL.using(getJdbcTemplate().getDataSource(), SQLDialect.MYSQL);
SelectQuery q = ctx.selectQuery();
q.addSelect(field("entity_id").as("id"),);
q.addFrom(table("entity").as("e"));
[...]
}
As you can see from the above, I'm building and executing queries using JOOQ. Does Spring still take care of closing the ResultSet I get back from JOOQ, or do I somehow "bypass" Spring when I access the data source directly and pass the data source on to JOOQ?
Spring doesn't do anything with the objects generated from your DataSource, i.e. Connection, PreparedStatement, ResultSet. From a Spring (or generally from a DataSource perspective), you have to do that yourself.
However, jOOQ will always:
close Connection objects obtained from a DataSource. This is documented in jOOQ's DataSourceConnectionProvider
close PreparedStatement objects right after executing them - unless you explicitly tell jOOQ to keep an open reference through Query.keepStatement()
close ResultSet objects right after consuming them through any ResultQuery.fetchXXX() method - unless you explicitly want to keep an open Cursor with ResultQuery.fetchLazy()
By design, jOOQ inverses JDBC's default behaviour of keeping all resources open and having users tediously close them explicitly. jOOQ closes all resources eagerly (which is what people do 95% of the time) and allows you to explicitly keep resources open where this is useful for performance reasons.
See this page of the jOOQ manual for differences between jOOQ and JDBC.
Currently I made a connection to a database in this way:
MyClass.java
try {
DataSource datasource = JNDILoader.getDataSourceObject(pathToSource);
Class.forName("net.sourceforge.jtds.jdbc.Driver");
connection = datasource.getConnection();
stmt = connection.prepareStatement("{call storageProcedureXXX(?,?)}");
stmt.setString(1, "X");
stmt.setString(2, "Y");
result = stmt.executeQuery();
}catch (SQLException){
//TODO
}catch(Exception){
//TODO
}
That works for 1 class that makes the requests for the data, but , would be better if I create a singleton class and get the connection from it? (performance?, maintenability?, simplicity?). Which option would be better: Singleton vs StorageProcedures per each request?.
Note: At the end, the application (Restful Web Service) will need to connect to different databases to load data for different specialized classes, even , the classes would need loads data from plain text.
First of all you are mixing two different things: singleton and stored procedures. Singleton is design pattern, and stored procedures are procedures executed on database, typically encapsulating some business logic.
What you wrote is not really preferred way of connecting to database. If you have many request and create one connection for each request son you will have problems with too many connections to database. You should use connection pool. The most famous for Java is DBCP. Another one is c3p0.
For connection on different databases you should use something like Hibernate.
Stored procedure are executed on the database. You pass/retrieve data to/from it through the connection.
You have to check if it is thread safe (I don't think so), if you'll do concurrent calls or not.
Generally a stored procedure = 1 transaction happening in the database.
Why are you using stored procedure in the 1st place?
I'm reading some entities with Hibernate:
Criteria criteria = session.createCriteria(…);
List<Entity> list = (List<Entity>) criteria.list();
Now I'm iterating over this list and want to send every object inside a Runnable to an Executor. I therefore use a RunnableBean.
for (Entity entity : list) {
IRunnableBean runnableBean = (IRunnableBean)
applicationContext.getBean("myRunnableBean", IRunnableBean.class);
runnableBean.setEntity(entity);
executor.execute(runnableBean);
}
The RunnableBean looks like this:
RunnableBean implements IRunnableBean {
// Setter
#Transactional
void run() {
entity.getMyCollection();
}
}
When I'm accessing the collection, I'm getting a org.hibernate.LazyInitializationException (no session or session was closed).
In Spring's log I see that the transactional method run() is correctly added. What am I doing wrong?
I guess you are using Spring's OpenSessionInViewFilter. If so, this behaviour is expected. Filter puts the database connection in the thread local context which is not available in your RunnableBean.
As myCollection isn't loaded eagerly, Spring does not have access to the database connection inside RunnableBean and can't load it. You need to:
create an enclosing session wrapper in your RunnableBean;
pass the id of your collection to the RunnableBean instead of passing object and load the collection inside RunnableBean
Alternatively, you can make your entity to load myCollection eagerly but this will make the overall loading process slower.
Just add the following line within your already written for loop:
Hibernate.initialize(entity.getMyCollection());
This is load the collection eagerly instead of lazily: no LazyInitializationException anymore.
I would also guess (like #mindas) that the transaction is not available in your bean because it runs in a different thread than the one that holds the transaction. As far as my experience goes spring also uses thread locals to resolve scoped proxies, so these won't work either in a bean that is run asynchronously.
Basically I would try to avoid running logic that requires a transaction in an asynchronous fashion, since asynchronous calls run for a longer time (otherwise, why use async calls?) and this will block the transaction and/or lead to timeouts.
The criteria api from jpa offers ways to fetch a relation eagerly only for a specific query. Maybe that could be a choice? Otherwise accessing the size() method of a collection will initialize it.