HHH-4661 - Properly propagate Query.setLockMode to Hibernate Core

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18682 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2010-02-02 21:32:53 +00:00
parent 889470af88
commit 1591e6dc03
11 changed files with 271 additions and 94 deletions

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.action;
@ -33,8 +32,7 @@ import org.hibernate.persister.entity.EntityPersister;
*
* @author Scott Marlow
*/
public class EntityIncrementVersionProcess implements BeforeTransactionCompletionProcess
{
public class EntityIncrementVersionProcess implements BeforeTransactionCompletionProcess {
private final Object object;
private final EntityEntry entry;

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,23 +20,22 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.action;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.EntityEntry;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.HibernateException;
import org.hibernate.OptimisticLockException;
import org.hibernate.engine.EntityEntry;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
/**
* Verify/Increment the entity version
*
* @author Scott Marlow
*/
public class EntityVerifyVersionProcess implements BeforeTransactionCompletionProcess
{
public class EntityVerifyVersionProcess implements BeforeTransactionCompletionProcess {
/** @noinspection FieldCanBeLocal,UnusedDeclaration */
private final Object object;
private final EntityEntry entry;
@ -53,12 +52,13 @@ public class EntityVerifyVersionProcess implements BeforeTransactionCompletionPr
public void doBeforeTransactionCompletion(SessionImplementor session) {
final EntityPersister persister = entry.getPersister();
Object latestVersion = persister.getCurrentVersion(
entry.getId(), session
);
if(!entry.getVersion().equals(latestVersion))
Object latestVersion = persister.getCurrentVersion( entry.getId(), session );
if ( !entry.getVersion().equals( latestVersion ) ) {
throw new OptimisticLockException(
"Newer version ("+ latestVersion+
") of entity ("+entry.getEntityName()+") found in database. id=" + entry.getId());
"Newer version [" + latestVersion +
"] of entity [" + MessageHelper.infoString( entry.getEntityName(), entry.getId() ) +
"] found in database"
);
}
}
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.dialect.lock;
@ -30,13 +29,11 @@ import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.hibernate.LockMode;
import org.hibernate.StaleObjectStateException;
import org.hibernate.event.EventSource;
import org.hibernate.action.EntityIncrementVersionProcess;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.EntityEntry;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.event.EventSource;
import org.hibernate.persister.entity.Lockable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An optimistic locking strategy that forces an increment of the version (after verifying that version hasn't changed).
@ -44,20 +41,18 @@ import org.slf4j.LoggerFactory;
* <p/>
* This strategy is valid for LockMode.OPTIMISTIC_FORCE_INCREMENT
*
* @since 3.5
*
* @author Scott Marlow
* @since 3.5
*/
public class OptimisticForceIncrementLockingStrategy implements LockingStrategy {
private static final Logger log = LoggerFactory.getLogger( OptimisticForceIncrementLockingStrategy.class );
private final Lockable lockable;
private final LockMode lockMode;
/**
/**
* Construct locking strategy.
*
* @param lockable The metadata for the entity to be locked.
* @param lockMode Indictates the type of lock to be acquired.
* @param lockMode Indicates the type of lock to be acquired.
*/
public OptimisticForceIncrementLockingStrategy(Lockable lockable, LockMode lockMode) {
this.lockable = lockable;
@ -67,22 +62,22 @@ public class OptimisticForceIncrementLockingStrategy implements LockingStrategy
}
}
/**
/**
* @see LockingStrategy#lock
*/
public void lock(
Serializable id,
Object version,
Object object,
int timeout, SessionImplementor session) throws StaleObjectStateException, JDBCException {
Serializable id,
Object version,
Object object,
int timeout, SessionImplementor session) throws StaleObjectStateException, JDBCException {
if ( !lockable.isVersioned() ) {
throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" );
}
EntityEntry entry = session.getPersistenceContext().getEntry(object);
EntityIncrementVersionProcess incrementVersion = new EntityIncrementVersionProcess(object, entry);
EventSource source = (EventSource)session;
EntityEntry entry = session.getPersistenceContext().getEntry( object );
EntityIncrementVersionProcess incrementVersion = new EntityIncrementVersionProcess( object, entry );
EventSource source = (EventSource) session;
// Register the EntityIncrementVersionProcess action to run just prior to transaction commit.
source.getActionQueue().registerProcess(incrementVersion);
source.getActionQueue().registerProcess( incrementVersion );
}
protected LockMode getLockMode() {

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.dialect.lock;
@ -30,25 +29,20 @@ import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.hibernate.LockMode;
import org.hibernate.StaleObjectStateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.EntityEntry;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.persister.entity.Lockable;
/**
* A pessimistic locking strategy that increments the version immediately (obtaining an exclusive write lock).
* <p/>
* This strategy is valid for LockMode.PESSIMISTIC_FORCE_INCREMENT
*
* @since 3.5
*
* @author Scott Marlow
* @since 3.5
*/
public class PessimisticForceIncrementLockingStrategy implements LockingStrategy {
private static final Logger log = LoggerFactory.getLogger( PessimisticForceIncrementLockingStrategy.class );
private final Lockable lockable;
private final LockMode lockMode;
@ -56,7 +50,7 @@ public class PessimisticForceIncrementLockingStrategy implements LockingStrategy
* Construct locking strategy.
*
* @param lockable The metadata for the entity to be locked.
* @param lockMode Indictates the type of lock to be acquired.
* @param lockMode Indicates the type of lock to be acquired.
*/
public PessimisticForceIncrementLockingStrategy(Lockable lockable, LockMode lockMode) {
this.lockable = lockable;
@ -67,26 +61,29 @@ public class PessimisticForceIncrementLockingStrategy implements LockingStrategy
}
}
/**
* @see org.hibernate.dialect.lock.LockingStrategy#lock
/**
* {@inheritDoc}
*/
public void lock(
Serializable id,
Object version,
Object object,
int timeout,
SessionImplementor session) throws StaleObjectStateException, JDBCException {
Serializable id,
Object version,
Object object,
int timeout,
SessionImplementor session) throws StaleObjectStateException, JDBCException {
if ( !lockable.isVersioned() ) {
throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" );
}
EntityEntry entry = session.getPersistenceContext().getEntry(object);
final EntityPersister persister = entry.getPersister();
Object nextVersion = persister.forceVersionIncrement(
entry.getId(), entry.getVersion(), session
);
entry.forceLocked( object, nextVersion );
EntityEntry entry = session.getPersistenceContext().getEntry( object );
final EntityPersister persister = entry.getPersister();
Object nextVersion = persister.forceVersionIncrement( entry.getId(), entry.getVersion(), session );
entry.forceLocked( object, nextVersion );
}
/**
* Retrieve the specific lock mode defined.
*
* @return The specific lock mode.
*/
protected LockMode getLockMode() {
return lockMode;
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,22 +20,54 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.event.def;
import org.hibernate.AssertionFailure;
import org.hibernate.LockMode;
import org.hibernate.action.EntityIncrementVersionProcess;
import org.hibernate.action.EntityVerifyVersionProcess;
import org.hibernate.classic.Lifecycle;
import org.hibernate.engine.EntityEntry;
import org.hibernate.event.PostLoadEvent;
import org.hibernate.event.PostLoadEventListener;
import org.hibernate.persister.entity.EntityPersister;
/**
* Call <tt>Lifecycle</tt> interface if necessary
* We do 2 things here:<ul>
* <li>Call {@link Lifecycle} interface if necessary</li>
* <li>Perform needed {@link EntityEntry#getLockMode()} related processing</li>
* </ul>
*
* @author Gavin King
* @author Steve Ebersole
*/
public class DefaultPostLoadEventListener implements PostLoadEventListener {
public void onPostLoad(PostLoadEvent event) {
final Object entity = event.getEntity();
final EntityEntry entry = event.getSession().getPersistenceContext().getEntry( entity );
if ( entry == null ) {
throw new AssertionFailure( "possible non-threadsafe access to the session" );
}
final LockMode lockMode = entry.getLockMode();
if ( LockMode.PESSIMISTIC_FORCE_INCREMENT.equals( lockMode ) ) {
final EntityPersister persister = entry.getPersister();
Object nextVersion = persister.forceVersionIncrement(
entry.getId(), entry.getVersion(), event.getSession()
);
entry.forceLocked( entity, nextVersion );
}
else if ( LockMode.OPTIMISTIC_FORCE_INCREMENT.equals( lockMode ) ) {
EntityIncrementVersionProcess incrementVersion = new EntityIncrementVersionProcess( entity, entry );
event.getSession().getActionQueue().registerProcess( incrementVersion );
}
else if ( LockMode.OPTIMISTIC.equals( lockMode ) ) {
EntityVerifyVersionProcess verifyVersion = new EntityVerifyVersionProcess( entity, entry );
event.getSession().getActionQueue().registerProcess( verifyVersion );
}
if ( event.getPersister().implementsLifecycle( event.getSession().getEntityMode() ) ) {
//log.debug( "calling onLoad()" );
( ( Lifecycle ) event.getEntity() ).onLoad( event.getSession(), event.getId() );

View File

@ -136,6 +136,27 @@ public class FromClause extends HqlSqlWalkerNode implements HqlSqlTokenTypes, Di
return fromElement;
}
public FromElement findFromElementBySqlAlias(String sqlAlias) {
FromElement fromElement = ( FromElement ) fromElementByTableAlias.get( sqlAlias );
if ( fromElement == null && parentFromClause != null ) {
fromElement = parentFromClause.getFromElement( sqlAlias );
}
return fromElement;
}
public FromElement findFromElementByUserOrSqlAlias(String userAlias, String sqlAlias) {
FromElement fromElement = null;
if ( userAlias != null ) {
fromElement = getFromElement( userAlias );
}
if ( fromElement == null ) {
fromElement = findFromElementBySqlAlias( sqlAlias );
}
return fromElement;
}
private FromElement findIntendedAliasedFromElementBasedOnCrazyJPARequirements(String specifiedAlias) {
Iterator itr = fromElementByClassAlias.entrySet().iterator();
while ( itr.hasNext() ) {

View File

@ -643,10 +643,15 @@ public abstract class Loader {
}
}
applyPostLoadLocks( row, lockModesArray, session );
return getResultColumnOrRow( row, queryParameters.getResultTransformer(), resultSet, session );
}
protected void applyPostLoadLocks(Object[] row, LockMode[] lockModesArray, SessionImplementor session) {
}
/**
* Read any collection elements contained in a single row of the result set
*/

View File

@ -39,6 +39,7 @@ import org.hibernate.QueryException;
import org.hibernate.ScrollableResults;
import org.hibernate.LockOptions;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.EntityEntry;
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor;
@ -281,43 +282,50 @@ public class QueryLoader extends BasicLoader {
* @param lockOptions a collection of lock modes specified dynamically via the Query interface
*/
protected LockMode[] getLockModes(LockOptions lockOptions) {
if ( lockOptions == null ||
lockOptions.getAliasLockCount() == 0 ) {
if ( lockOptions == null ) {
return defaultLockModes;
}
else {
// unfortunately this stuff can't be cached because
// it is per-invocation, not constant for the
// QueryTranslator instance
LockMode[] lockModesArray = new LockMode[entityAliases.length];
for ( int i = 0; i < entityAliases.length; i++ ) {
LockMode lockMode = lockOptions.getEffectiveLockMode( entityAliases[i] );
if ( lockMode == null ) {
//NONE, because its the requested lock mode, not the actual!
lockMode = LockMode.NONE;
}
lockModesArray[i] = lockMode;
}
return lockModesArray;
if ( lockOptions.getAliasLockCount() == 0
&& ( lockOptions.getLockMode() == null || LockMode.NONE.equals( lockOptions.getLockMode() ) ) ) {
return defaultLockModes;
}
// unfortunately this stuff can't be cached because
// it is per-invocation, not constant for the
// QueryTranslator instance
LockMode[] lockModesArray = new LockMode[entityAliases.length];
for ( int i = 0; i < entityAliases.length; i++ ) {
LockMode lockMode = lockOptions.getEffectiveLockMode( entityAliases[i] );
if ( lockMode == null ) {
//NONE, because its the requested lock mode, not the actual!
lockMode = LockMode.NONE;
}
lockModesArray[i] = lockMode;
}
return lockModesArray;
}
protected String applyLocks(String sql, LockOptions lockOptions, Dialect dialect) throws QueryException {
// can't cache this stuff either (per-invocation)
// we are given a map of user-alias -> lock mode
// create a new map of sql-alias -> lock mode
if ( lockOptions == null ||
( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) {
return sql;
}
// can't cache this stuff either (per-invocation)
// we are given a map of user-alias -> lock mode
// create a new map of sql-alias -> lock mode
// we need both the set of locks and the columns to reference in locks
// as the ultimate output of this section...
final LockOptions locks = new LockOptions( lockOptions.getLockMode() );
final Map keyColumnNames = dialect.forUpdateOfColumns() ? new HashMap() : null;
locks.setScope( lockOptions.getScope() );
locks.setTimeOut( lockOptions.getTimeOut() );
final Map keyColumnNames = dialect.forUpdateOfColumns() ? new HashMap() : null;
final Iterator itr = sqlAliasByEntityAlias.entrySet().iterator();
while ( itr.hasNext() ) {
final Map.Entry entry = (Map.Entry) itr.next();
@ -333,7 +341,9 @@ public class QueryLoader extends BasicLoader {
// want to apply the lock against the root table (for all other strategies,
// it just happens that driving and root are the same).
final QueryNode select = ( QueryNode ) queryTranslator.getSqlAST();
final Lockable drivingPersister = ( Lockable ) select.getFromClause().getFromElement( userAlias ).getQueryable();
final Lockable drivingPersister = ( Lockable ) select.getFromClause()
.findFromElementByUserOrSqlAlias( userAlias, drivingSqlAlias )
.getQueryable();
final String sqlAlias = drivingPersister.getRootTableAlias( drivingSqlAlias );
final LockMode effectiveLockMode = lockOptions.getEffectiveLockMode( userAlias );
@ -344,9 +354,26 @@ public class QueryLoader extends BasicLoader {
}
}
// apply the collected locks and columns
return dialect.applyLocksToSql( sql, locks, keyColumnNames );
}
protected void applyPostLoadLocks(Object[] row, LockMode[] lockModesArray, SessionImplementor session) {
// todo : scalars???
// if ( row.length != lockModesArray.length ) {
// return;
// }
//
// for ( int i = 0; i < lockModesArray.length; i++ ) {
// if ( LockMode.OPTIMISTIC_FORCE_INCREMENT.equals( lockModesArray[i] ) ) {
// final EntityEntry pcEntry =
// }
// else if ( LockMode.PESSIMISTIC_FORCE_INCREMENT.equals( lockModesArray[i] ) ) {
//
// }
// }
}
protected boolean upgradeLocks() {
return true;
}

View File

@ -259,7 +259,7 @@ public abstract class AbstractQueryImpl<X> implements TypedQuery<X> {
}
else {
// extract the alias
final String alias = hintName.substring( AvailableSettings.ALIAS_SPECIFIC_LOCK_MODE.length() );
final String alias = hintName.substring( AvailableSettings.ALIAS_SPECIFIC_LOCK_MODE.length() + 1 );
// determine the LockMode
try {
final LockMode lockMode = LockModeTypeHelper.interpretLockMode( value );

View File

@ -15,6 +15,13 @@ public class Lock {
private Integer version;
private String name;
public Lock() {
}
public Lock(String name) {
this.name = name;
}
@Id
@GeneratedValue
public Integer getId() {

View File

@ -0,0 +1,95 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.ejb.test.lock;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.ejb.AvailableSettings;
import org.hibernate.ejb.QueryImpl;
import org.hibernate.ejb.test.TestCase;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class QueryLockingTest extends TestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Lock.class };
}
public void testOverallLockMode() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
QueryImpl jpaQuery = em.createQuery( "from Lock_ l" ).unwrap( QueryImpl.class );
org.hibernate.impl.QueryImpl hqlQuery = (org.hibernate.impl.QueryImpl) jpaQuery.getHibernateQuery();
assertEquals( LockMode.NONE, hqlQuery.getLockOptions().getLockMode() );
assertNull( hqlQuery.getLockOptions().getAliasSpecificLockMode( "l" ) );
assertEquals( LockMode.NONE, hqlQuery.getLockOptions().getEffectiveLockMode( "l" ) );
// NOTE : LockModeType.READ should map to LockMode.OPTIMISTIC
jpaQuery.setLockMode( LockModeType.READ );
assertEquals( LockMode.OPTIMISTIC, hqlQuery.getLockOptions().getLockMode() );
assertNull( hqlQuery.getLockOptions().getAliasSpecificLockMode( "l" ) );
assertEquals( LockMode.OPTIMISTIC, hqlQuery.getLockOptions().getEffectiveLockMode( "l" ) );
jpaQuery.setHint( AvailableSettings.ALIAS_SPECIFIC_LOCK_MODE+".l", LockModeType.PESSIMISTIC_WRITE );
assertEquals( LockMode.OPTIMISTIC, hqlQuery.getLockOptions().getLockMode() );
assertEquals( LockMode.PESSIMISTIC_WRITE, hqlQuery.getLockOptions().getAliasSpecificLockMode( "l" ) );
assertEquals( LockMode.PESSIMISTIC_WRITE, hqlQuery.getLockOptions().getEffectiveLockMode( "l" ) );
em.getTransaction().commit();
em.close();
}
public void testForcedIncrementOverall() {
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
Lock lock = new Lock( "name" );
em.persist( lock );
em.getTransaction().commit();
em.close();
Integer initial = lock.getVersion();
assertNotNull( initial );
em = getOrCreateEntityManager();
em.getTransaction().begin();
Lock reread = em.createQuery( "from Lock_", Lock.class ).setLockMode( LockModeType.PESSIMISTIC_FORCE_INCREMENT ).getSingleResult();
em.getTransaction().commit();
em.close();
assertFalse( reread.getVersion().equals( initial ) );
em = getOrCreateEntityManager();
em.getTransaction().begin();
em.remove( em.getReference( Lock.class, reread.getId() ) );
em.getTransaction().commit();
em.close();
}
}