JPA 2.0 : Adding entity classes to PersistenceUnit *from different jar* automatically

There are several way to solve it:

  1. As described in Do I need <class> elements in persistence.xml?, you can set hibernate.archive.autodetection property and Hibernate should be able to look up all annotated classes from classpath. However, that’s not JPA spec compliant.
  2. If you are using Spring, from Spring 3.1.2 (or probably even a bit earlier), in LocalContainerEntityManagerFactoryBean, you can define packageToScan which will ask LocalContainerEntityManagerFactoryBean to scan in classpath to find all annotated classes. Again, not JPA spec compliant.
Advertisements

Override Optimistic Lock and persist stale entity in Hibernate

In real time application, There could be a scenario to tell the hibernate not to allow concurrent update of an entity. Optimistic lock can be used to accomplish it, It never locks the underlying record in the DB rather it needs either Version or Timestamp column to determine whether the entity is up-to-date or stale.

 

@Column( name = "VERS", nullable = false )
@Version

private Long vers;

Version column is being updated automatically whenever entity is been udpated. Hibernate uses this version column value to check for stale. Second concurrent process which is updating this entity will receive OptimisticLockException/StaleObjectException.

If you want stale object to be updated  by any cause, use the following Spring AOP approach.Configure the following beans in your spring-beans.xml

 <bean id="retryingTransactionInterceptor">    
  <property name="order" value="1" />
  </bean>  
  <aop:config>     
   <aop:aspect id="retryingTransactionAspect" ref="retryingTransactionInterceptor">         
     <aop:pointcut  id="servicesWithRetryingTransactionAnnotation" expression="execution( * examples.service..*.*(..) ) 
     and @annotation(examples.annotation.RetryingTransaction)"/>         
     <aop:around method="retryOperation" pointcut-ref="servicesWithRetryingTransactionAnnotation"/>     
   </aop:aspect> 
  </aop:config>

 

 
package examples.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target( {
 ElementType.METHOD
} )
@Retention( RetentionPolicy.RUNTIME )
public @interface RetryingTransaction {
 int repeatCount() default 20;
}

 

@Transactional
 @RetryingTransaction
 public Test merge( Test test ) {
  return testDao.merge( test );
 }

 

package examples;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import javax.persistence.OptimisticLockException;
import javax.persistence.Version;
import org.aspectj.lang.ProceedingJoinPoint;
import org.hibernate.property.BasicPropertyAccessor;
import org.springframework.core.Ordered;
public class RetryingTransactionInterceptor implements Ordered {
 private static final int DEFAULT_MAX_RETRIES = 20;
 private int maxRetries = DEFAULT_MAX_RETRIES;
 private int order = 1;
 public void setMaxRetries( int maxRetries ) {
  this.maxRetries = maxRetries;
 }
 public int getOrder() {
  return this.order;
 }
 public void setOrder( int order ) {
  this.order = order;
 }
 public Object retryOperation( ProceedingJoinPoint pjp ) throws Throwable {
  int numAttempts = 0;
  Exception failureException = null;
  Object obj = null;
  Object arg1 = null;
  do {
   numAttempts++;
   try {
    Object[] obje = pjp.getArgs();
    arg1 = (Test) obje[ 0 ];
    obj = pjp.proceed();
    return obj;
   } catch ( RuntimeException re ) {
    failureException = re;
    System.out.println( "OptimisticLockException:"
      + ( (OptimisticLockException) re.getCause() ).getEntity() );
    incrementVersion( arg1 );
   }
  } while ( numAttempts <= this.maxRetries );
  throw failureException;
 }
 private Object incrementVersion( Object entityObject ) throws IllegalAccessException,
  InvocationTargetException, NoSuchMethodException {
  Object idValue = null;
  for ( Field field : entityObject.getClass().getDeclaredFields() ) {
   BasicPropertyAccessor basicGetter = new BasicPropertyAccessor();
   if ( field.getAnnotation( Version.class ) != null ) {
    System.out.println( "Id value:"
      + basicGetter.getGetter( entityObject.getClass(), field.getName() ).get(
       entityObject ) );
    idValue =
      basicGetter.getGetter( entityObject.getClass(), field.getName() ).get(
       entityObject );
    basicGetter.getSetter( entityObject.getClass(), field.getName() ).set(
     entityObject, (Long) idValue + 1, null );
    break;
   }
  }
  return idValue;
 }
}

Above code catches the OptimisticLock ,increase the version value and persist the stale
entity . You can also achieve it using simple update query too. It is up to
you to take correct decision.  

 

How to lazy load one to one related entity in hibernate JPA?

Hibernate does not support one-to-one lazy loading.

Work around is marking one-to-many relationship in parent end and one-to-one in child end.

In Parent

@OneToMany( mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.ALL )
private List<Child> childs;

In Child

@OneToOne( )
@JoinColumn( name = "child_id", referencedColumnName = "parent_id" )
private Parent parent;

Merging two persistence units

We can merge two persistence unit if both have same persistence-unit name. This can be done using persistence unit post processor.

Note: “default” is the PU name for both persistence1.xml and persistence2.xml given below.

persistence1.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="default">
    <class>examples.A</class>
    .
    .
    .
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>

persistence2.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="default">
    <class>samples.A</class>
    .
    .
    .
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>

2. Persistence post processor is used to merge all entities present in persistence1.xml & persistence2.xml and be

part of persistence unit manager.

package examples;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MergingPersistenceUnitPostProcessor
implements PersistenceUnitPostProcessor {
Map<String, List<String>> puiClasses = new HashMap<String, List<String>>();

public void postProcessPersistenceUnitInfo( MutablePersistenceUnitInfo pui ) {
List<String> classes = puiClasses.get( pui.getPersistenceUnitName() );
if ( classes == null ) {
classes = new ArrayList<String>();
puiClasses.put( pui.getPersistenceUnitName(), classes );
}
pui.getManagedClassNames().addAll( classes );
final List<String> names = pui.getManagedClassNames();
classes.addAll( pui.getManagedClassNames() );
}
}

3. Configure the merging persistence unit post processor in persistence unit manager.

    <bean    class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
    <bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="websphereDataSource" />
        <property name="persistenceUnitManager" ref="pum" />
        <property name="jpaVendorAdapter">
            <bean
                class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="database" value="ORACLE" />
                <property name="showSql" value="true" />
            </bean>
        </property>
    </bean>

    <bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
        <property name="persistenceXmlLocations">
            <list>
             <value>classpath*:META-INF/persistence1.xml</value>
             <value>classpath*:META-INF/persistence2.xml</value>
            </list>
        </property>
        <property name="defaultDataSource" ref="websphereDataSource"/>
        <property name="persistenceUnitPostProcessors">
            <bean class="examples.MergingPersistenceUnitPostProcessor"/>
        </property>
    </bean>

Duplicate child inserts during merge operation in hibernate

Parent entity has list of child object marked as “LAZY” with CascadeType.ALL. When I tried to add child into parent and merge it, it tries to inserts two child records.

This issue can be fixed in following ways

Solution 1:

    Retrieve the child list size() to initialize lazy child collections, add the new child and merge it.

It will insert one child record only. Ensure that it is bounded in a transaction.

parent.getChilds().size();

    parent.getChilds().add(child);

    entityManager.merge(parent);

 

Solution 2:

    Simply add the new child into parent and don’t call merge operation. Ensure that it is bounded in a transaction.

    

parent.getChilds().add(child);

Pick up any solution that works out for you.

This is a bug in Hibernate marked as closed but not fixed https://hibernate.onjira.com/browse/HHH-3332.

How to instruct hibernate to set DB datetime for an entity attribute(s) automatically?

Use GenerationTime to instruct hibernate to set DB date time for an attribute.

E.g.

 @Column( nullable = false) 
    @Temporal( value = TemporalType.TIMESTAMP ) 
    @org.hibernate.annotations.Generated(value=GenerationTime.INSERT) 
    private Date crtdttm; 

 @Column( nullable = false ) 
    @Temporal( value = TemporalType.TIMESTAMP ) 
    @org.hibernate.annotations.Generated(value=GenerationTime.ALWAYS) 
    private Date upddttm; 

GenerationTime.INSERT – Set DB timestamp while Inserting only.

GenerationTime.ALWAYS – Set DB timestamp ALWAYS for all DML operations.

Null entities are not supported by org.hibernate.event.def.EventCache

Please ensure that you set all not nullable attributes of an entity before you perform merge operation.

E.G

@Column(name="name", length = 30, nullable = false)
private String name;

Ensure that name attribute is being set before calling entityManager.merge(<OBJECT>)