I want to delete and insert triples from a sesame repository using SPARQL queries, and I want to execute both operations as a single transaction.
DELETE
INSERT
If an exception is thrown during the transaction, rollback is executed... but it seems not to work.
The problem is that, if an exception is thrown during the insert query, the rollback is executed, but the previously deleted triples are not recovered (Why?).
Here you have some code:
I have a class called OwlimConnector which wraps the repository connection and provides some methods to make SPARQL Queries.
In the constructor of this class, I set up the connection and I set the autocommit to false:
RemoteRepositoryManager repos_manager = RemoteRepositoryManager.getInstance(SERVER_URL, USER, PASSWORD);
repos_manager.initialize();
Repository ssr = repos_manager.getRepository(REPOSITORY);
rconn = ssr.getConnection();
rconn.setAutoCommit(false);
In OwlimConnector there is a method called executeUpdate:
public void executeUpdate(String queryString) throws RepositoryException, MalformedQueryException, UpdateExecutionException
{
Update up = rconn.prepareUpdate(QueryLanguage.SPARQL, queryPrefixString + queryString);
up.execute();
}
and these methods among others:
public void commit(){
rconn.commit();
}
public void rollback() {
rconn.rollback();
}
public void close(){
rconn.close();
}
On the other hand, I have a web service "updateUserGroup" wich uses the previous OwlimConnector and a data access object called UserGroupDAO:
#PUT
#Consumes(MediaType.APPLICATION_XML)
public Response updateUserGroup(UserGroup ug) {
try {
oc = new OwlimConnector();
} catch (OwlimInstantiationException e) {
return ResponseFactory.getError(e.getErrorMessage());
}
try {
UserGroupDAO ugdao = new UserGroupDAO(oc);
ugdao.delete(ug.getUri());
ugdao.add(ug);
oc.commit();
oc.close();
return ResponseFactory.getOK();
} catch (MandatoryFieldException e) {
oc.rollback();
oc.close();
return ResponseFactory.getError(e.getErrorMessage());
} catch (NotExistingResourceException e) {
oc.rollback();
oc.close();
return ResponseFactory.getError(e.getErrorMessage());
} catch (Exception e) {
oc.rollback();
oc.close();
return ResponseFactory.getError(new GenericException(e).getErrorMessage());
}
}
1 What ugdao.delete(ug.getUri()) does is to call the OwlimConnector method executeUpdate:
oc.executeUpdate("DELETE { " + usergroup + " ?p ?v . } WHERE { " + usergroup + " ?p ?v . }");
Here the triples are deleted even though there is no commit!
2 What ugdao.add(ug) does is:
To check that ug.getName() is not null or spaces, otherwise the MandatoryFieldException is thrown:
if (ug.getName() == null || ug.getName().equals("")){
throw new MandatoryFieldException("name");
}
Then, the data is inserted:
oc.executeUpdate("INSERT DATA { " + ug.getUri() + " a scmu:UserGroup ; scmu:hasName \"" + ug.getName() + "\" . }");
When ug.getName() is null or spaces the MandatoryFieldException exception is thrown and caught by updateUserGroup. Then the rollback is executed but the deleted triples are not recovered.
I don´t know why this happens. Any Idea?
Thank you very much in advance
The solution is much easier than I thought. This is the answer I received from the Ontotext AD in the mailing list:
"you are using RemoteRepository so each update, is immediately sent to the remote repository at up.execute() and there it is autocommited immediately .
what you could do is instead of preparing and executing on each delete/add operation in your service to start collecting all individual updates (into a StringBuilder for instance) and on oc.commit() to prepare and execute the whole list of updates at once (and just clear the list on rollback, in case an exception is thrown)
Your update request can have multiple 'INSERT DATA' or 'DELETE DATA' updates ... "
And it works! Thank you.
Related
My problem is that JPA/Hibernate returns true for a call of entityManager.getTransaction().isActive() even when I did not explicitly start a transaction (see code below).
The problem here is that I want to read something from the database and a SerializationException is ok in this scenario, because this just indicates that the persisted object does not fit to the actual code any more and needs to be recalculated. Instead of just returning null the code below throws the following exception:
Transaction rollback failed.
org.hibernate.TransactionException: Unable to rollback against JDBC Connection
This shows me, there must have been a transaction somewhere which I did not start in my code. The finally block in the code above is
final EntityManager entityManager = Persistence.createEntityManagerFactory("test").createEntityManager();
try {
final TypedQuery<Test> query = entityManager.createQuery("SELECT t FROM test t", Test.class);
return query.getResultList();
} catch (final PersistenceException e) {
if (e.getCause() instanceof SerializationException) {
LOG.debug("Implementation changed meanwhile. That's ok - we return null.");
return null;
}
throw e;
} finally {
EntityManagerCloser.closeEntityManager(entityManager);
}
And the EntityManagerCloser looks like this:
public final class EntityManagerCloser {
private static final Logger LOG = LoggerFactory.getLogger(EntityManagerCloser.class);
public static void closeEntityManager(EntityManager entityManager) {
if (entityManager.getTransaction().isActive()) {
try {
entityManager.getTransaction().rollback();
} catch (PersistenceException | IllegalStateException e) {
LOG.error("Transaction rollback failed.", e);
}
}
if (entityManager.isOpen()) {
try {
entityManager.close();
} catch (IllegalStateException e) {
LOG.error("Closing entity manager failed.", e);
}
}
}
}
Hibernate docs says "Always use clear transaction boundaries, even for read-only operations". So do I really need to insert a
entityManager.getTransaction().begin();
....
<do read here>
....
entityManager.getTransaction().commit();
around every read operation I perform on the database?
I could implement another closeEntityManager method for read-only operations without the rollback transaction block but I want to understand why there IS a transaction at all. Thanks for any help!
The problem is that when you call entityManager.getTransaction(); a new transaction object will be created. So it is better to save the transaction reference to a variable as shown below.
Transaction txn = entityManager.getTransaction();
if (txn.isActive()) {
try {
txn.rollback();
} catch (PersistenceException | IllegalStateException e) {
LOG.error("Transaction rollback failed.", e);
}
}
Thanks to Jobin I quickly found the solution to my problem:
I think I need to call entityManager.isJoinedToTransaction() in my closeEntityManager method before calling entityManager.getTransaction().isActive().
This will prevent the EntityManagerCloser to start its own transaction which I can not rollback later because I did not explicitly call transaction.begin() on it.
I am using:
Web App (a filter opens session. DAO uses getCurrentSession())
Hibernate
Spring (AOP configuration over Service)
xml configuration for all
DTO between Mbean and Service
Well, I have two methods (business service):
service.findUser(..DTO..)
service.updateUser(..DTO..)
update throws org.hibernate.NonUniqueObjectException exception.
How can I avoid that?
I need to use update, not merge.
Thanks in advance.
//MBean.java method
public void testUpdateUser(ActionEvent e) {
System.out.println(name);
ServiceResponse<UserDto> serviceResponse = super.getPrincipalService().findUser(name);
UserDto userDto = serviceResponse.getResponseList().get(0);
//update some properties here
serviceResponse = super.getPrincipalService().updateUser(userDto);
LOG.info("" + serviceResponse);
}
//Service.java: update method
public ServiceResponse<UserDto> updateUser(UserDto userDto) {
LOG.info("");
ServiceResponse<UserDto> serviceResponse = new ServiceResponse<UserDto>();
try {
User user = this.getGlobalMapper().map(userDto, User.class);
//
this.getUserDao().update(user);
userDto = this.getGlobalMapper().map(user, UserDto.class);
serviceResponse.getResponseList().add(userDto);
serviceResponse.setOperationCodeResponse(ServiceResponseCode.OK);
serviceResponse.getMessages().add("Operacion OK");
} catch (Exception e) {
serviceResponse.getMessages().add(e.getMessage());
serviceResponse.setOperationCodeResponse(ServiceResponseCode.MODEL_ERROR);
LOG.error("", e);
}
return serviceResponse;
}
//Exception result
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.softlogia.copi.model.domain.User#155]
at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:696)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:296)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:241)
at org.hibernate.event.internal.DefaultUpdateEventListener.performSaveOrUpdate(DefaultUpdateEventListener.java:55)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
at org.hibernate.internal.SessionImpl.fireUpdate(SessionImpl.java:705)
at org.hibernate.internal.SessionImpl.update(SessionImpl.java:697)
at org.hibernate.internal.SessionImpl.update(SessionImpl.java:693)
I am assuming you are using pure Hibernate as ORM; simply put, regardless of the status of your db, you have in your current Hibernate session different copies of the same row. To resolve this you can:
1) flush() the hibernate session after every writing operation on db (insert or update)
OR
2) In your update metod call merge() instead of saveOrUpdate()
I got a deadlock problem with mysql and an application that I am developing. The application, based on spring boot, integration and jpa, has different threads and all of them can access this service:
#Override
#Transactional()
public List<TwitterUser> storeTwitterUsers(List<TwitterUser> twitterUsers)
{
logger.debug("Store list of users, total: " + twitterUsers.size());
List<TwitterUser> savedUsers = new ArrayList<>();
for ( TwitterUser twitterUser : twitterUsers ) {
TwitterUser user = getTwitterUserById(twitterUser.getTwitterId());
if ( user != null ) {
twitterUser.setId(user.getId());
user = entityManager.merge(twitterUser);
} else {
//HERE THE EXCEPTION IS THROWN
entityManager.persist(twitterUser);
user = twitterUser;
}
entityManager.flush();
savedUsers.add(user);
}
return savedUsers;
}
#Override
#Transactional(readOnly = true)
public TwitterUser getTwitterUserById(Long id)
{
Query query = entityManager.createQuery("from TwitterUser u where u.twitterId=:id");
query.setParameter("id", id);
TwitterUser twitterUser = null;
//Throw Exception NoResultException
try {
twitterUser = (TwitterUser)query.getSingleResult();
} catch (NoResultException e) {
//no result found
}
return twitterUser;
}
When more than one thread is within the method storeTwitterUsers, mysql throw this error:
Deadlock found when trying to get lock; try restarting transaction
This is the full stack track of the error:
http://pastebin.com/nZEvykux
I already read those two questions:
How to avoid mysql 'Deadlock found when trying to get lock; try restarting transaction'
Getting "Deadlock found when trying to get lock; try restarting transaction"
but my problem seems slightly different because I got the exception when almost any thread tries to persist the object.
Is there a clean and easy way to resolve the problem without implementing a low level code check? Can Spring JPA automatically manage the deadlock situation?
Any help is really appreciated, I am struggling with that error!
I have a Java application where I want to be able to kill queries made to several databases. I know how to get the queries in the information_schema.processlist, but the problem is I don't know how to kill those queries. Usually I would do it using KILL command, but I cant use it in java application (or at least I havent figured out how to do it).
I've been reading about the Statement.cancel but the problem is that the queries are created by another application. I've understood that you have to have Statement variable in the class where you want to cancel it.
if I can use Statement for this purpose, can someone please help me to understand how. I'm using MySQL database. I dont want to set timeout, because I want to be able to kill any query I want to.
These are some examples how I've tried to kill the process:
public synchronized void killProcesses(Set<Long> ids) {
for (long id : ids) {
String killCommand="Select 'KILL ' from processlist where id=:id";
Query query= queryManager.createQuery(killCommand);
query.setParameter("id", id);
query.getResultList();
}
}
public synchronized void killProcesses(Set<Long> ids) {
for (long id : ids) {
String killCommand="KILL " + id;
Query query= queryManager.createQuery(killCommand);
query.getResultList();
}
}
#HannoBinder solved the problem, the final solution is:
private String killQuery(long id) {
String killCommand="KILL " + id;
queryManager.getTransaction().begin();
Query query=queryManager.createNativeQuery(killCommand);
query.executeUpdate();
queryManager.getTransaction().commit();
return "OK";
}
And one that worked too, but isnt that simple:
public synchronized void killProcesses(Set<Long> ids) {
for (long id : ids) {
String killCommand="KILL QUERY " + id;
queryManager.getTransaction().begin();
Connection conn=queryManager.unwrap(Connection.class);
try {
Statement statement= conn.createStatement();
statement.execute(killCommand);
} catch (SQLException e) {
e.printStackTrace();
} finally {
queryManager.getTransaction().commit();
conn.close();
}
}
}
I’m writing in order to get some help.
To be short, I’m trying to use com.unboundid.ldap.sdk (but it is not necessary - the same problem i get if i use oracle's javax.naming.ldap.*) to handle with ldap transactions, and I get the following error:
Exception in thread "Main Thread" java.lang.AssertionError: Result EndTransactionExtendedResult(resultCode=2 (protocol error), diagnosticMessage='protocol error') did not have the expected result code of '0 (success)'.
at com.unboundid.util.LDAPTestUtils.assertResultCodeEquals(LDAPTestUtils.java:1484)
at pkg.Main.main(Main.java:116)
My program is the following ( I’m using simple example from https://www.unboundid.com/products/ldap-sdk/docs/javadoc/com/unboundid/ldap/sdk/extensions/StartTransactionExtendedRequest.html ) :
public class Main {
public static void main( String[] args ) throws LDAPException {
LDAPConnection connection = null;
try {
connection = new LDAPConnection("***", ***, "***", "***");
} catch (LDAPException e1) {
e1.printStackTrace();
}
// Use the start transaction extended operation to begin a transaction.
StartTransactionExtendedResult startTxnResult;
try
{
startTxnResult = (StartTransactionExtendedResult)
connection.processExtendedOperation(
new StartTransactionExtendedRequest());
// This doesn't necessarily mean that the operation was successful, since
// some kinds of extended operations return non-success results under
// normal conditions.
}
catch (LDAPException le)
{
// For an extended operation, this generally means that a problem was
// encountered while trying to send the request or read the result.
startTxnResult = new StartTransactionExtendedResult(
new ExtendedResult(le));
}
LDAPTestUtils.assertResultCodeEquals(startTxnResult, ResultCode.SUCCESS);
ASN1OctetString txnID = startTxnResult.getTransactionID();
// At this point, we have a transaction available for use. If any problem
// arises, we want to ensure that the transaction is aborted, so create a
// try block to process the operations and a finally block to commit or
// abort the transaction.
boolean commit = false;
try
{
// do nothing
}
finally
{
// Commit or abort the transaction.
EndTransactionExtendedResult endTxnResult;
try
{
endTxnResult = (EndTransactionExtendedResult)
connection.processExtendedOperation(
new EndTransactionExtendedRequest(txnID, commit));
}
catch (LDAPException le)
{
endTxnResult = new EndTransactionExtendedResult(new ExtendedResult(le));
}
LDAPTestUtils.assertResultCodeEquals(endTxnResult, ResultCode.SUCCESS);
}
}
}
As you can see, I do nothing with the transaction: just start and rolling back, but it still not working.
Connection is ok, and I receive transaction id = F10285501E20C32AE040A8C0070F7502 BUT IT ALWAYS THE SAME - is it all wrigth???
If “// do nothing” replace with some action exception: unwilling to perform.
I’m starting to think that it is OID problem, but I just can’t figure out what is wrong…
OID is on a WebLogic server and it’s version is :
Version Information
ODSM 11.1.1.6.0
OID 11.1.1.6.0
DB 11.2.0.2.0
All ideas will be appreciated.