Configurations:
- Mojarra 2.1.24, Spring 3.2.4.RELEASE, Hibernate 4.2.4.FINAL.
- I have 4 layers: MODEL, DAO, SERVICE and CONTROLLER (JSF BEANS)
app-config.xml:
Code:
<beans ...>
<!-- Reading Spring Annotations -->
<context:component-scan base-package="com.scrummps" />
<mvc:annotation-driven />
<!-- Activates annotation-based bean configuration -->
<context:annotation-config/>
<!-- Hibernate Config -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="hibernateProperties">
<props>
<prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.connection.autocommit">false</prop>
<prop key="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
<prop key="hibernate.c3p0.max_size">100</prop>
<prop key="hibernate.c3p0.min_size">5</prop>
<prop key="hibernate.c3p0.timeout">3000</prop>
<prop key="hibernate.jdbc.batch_size">50</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.provider_configuration_file_resource_path">\WEB-INF\ehcache.xml</prop>
<prop key="hibernate.cache.use_structured_entries">true</prop>
</props>
</property>
<!-- Those package will be scanned for classes with persistence annotations -->
<property name="packagesToScan" value="com.scrummps" />
<!-- Annotated package. Contains package-level configuration. -->
<property name="annotatedPackages" value="com.scrummps" />
</bean>
<!-- Transaction Manager is defined -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- Enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
Item.java Item model:
Code:
@Audited(withModifiedFlag = true)
@DynamicInsert(true)
@SelectBeforeUpdate(true)
@DynamicUpdate(true)
@Entity
@Table(name = "Item")
public class Item extends AbstractEntity {
...
}
CommonDAOImpl.java Common DAO using for ItemDAO:
Code:
public class CommonDAOImpl<T extends AbstractEntity> implements CommonDAO<T> {
/** Class to map */
private Class<T> clazz;
/** Session factory */
@Autowired
private SessionFactory sessionFactory;
public CommonDAOImpl(final Class<T> clazzToSet) {
this.clazz = clazzToSet;
}
@Override
public T findOne(final Long id) throws AppException {
try {
return (T) getSession().get(getClazz(), id);
} catch (Exception e) {
logger.error(createLogMessage(EnumMessages.ERROR_FIND_ONE), e);
throw new AppException(EnumMessages.ERROR_FIND_ONE);
}
}
@Override
public void update(final T entity) throws AppException {
try {
getSession().update(entity);
} catch (Exception e) {
logger.error(createLogMessage(EnumMessages.ERROR_UPDATE), e);
throw new AppException(EnumMessages.ERROR_UPDATE);
}
}
@Override
public Session getSession() {
return sessionFactory.getCurrentSession();
}
public Class<T> getClazz() {
return clazz;
}
}
ItemService.java Item Service:
Code:
@Service("itemService")
public class ItemServiceImpl extends CommonItemServiceImpl<Item> implements ItemService {
@Autowired
private ItemDAO itemDAO;
@Override
public CommonItemDAO<Item> getDao() throws AppException {
return (CommonItemDAO<Item>) itemDAO;
}
}
ItemController.java Controller of xhtml page:
Code:
@ManagedBean
@ViewScoped
public class ItemController extends CommonController implements Serializable {
private static final long serialVersionUID = 1L;
/** Item service */
@ManagedProperty(value="#{itemService}")
private ItemService itemService;
private Item item;
@PostConstruct
public void initComponent() {
item = itemService.findOne((long) 1); // Example
// If I update the entity here, the Hibernate Envers works PERFECTLY and mark the properties's flag correctly
// item.setStatus(EnumStatus.DONE);
// itemService.update(item);
}
/**
* Method called by xhtml page
*/
public void update(Item item) {
itemService.update(item);
}
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
public ItemService getItemService() {
return itemService;
}
public void setItemService(ItemService itemService) {
this.itemService = itemService;
}
}
Flow of execution:
- Load the page xhtml. The PostConstruct method load the Item 1 and populate the fields in page form. When I call the function "update(item)" by ajax button in xhtml page, the Controller of page call the Service's layer that finally call DAO's layer.
Result:
- Item entity updated with success;
- Item_AUD updated with success, but ALL modified flags are set with TRUE. It's wrong! :(
In my debug, I saw that the event.getOldState() in the method onPostUpdate(Event event) of class org.hibernate.envers.event.EnversPostUpdateEventListenerImpl was with value NULL. Therefore ALL the modified flags are set with TRUE.
This can be a problem of my Spring configuration? a bug in Hibernate Envers? or something about Spring OpenSessionInViewFilter with JSF?
Thanks for the help.