Buscar este blog

Mostrando entradas con la etiqueta Hibernate. Mostrar todas las entradas
Mostrando entradas con la etiqueta Hibernate. Mostrar todas las entradas

sábado, 3 de febrero de 2018

JBoss EAP 6.4 and Hibernate 5 - JBoss Logging problems

Problem

When working with Hibernate 5 in JBoss EAP 6.4 you can get the following error: java.lang.NoSuchMethodError: org.jboss.logging.Logger.debugf(Ljava/lang/String;I)V
Caused by: java.lang.NoSuchMethodError: org.jboss.logging.Logger.debugf(Ljava/lang/String;I)V
 at org.hibernate.internal.NamedQueryRepository.checkNamedQueries(NamedQueryRepository.java:149)
 at org.hibernate.internal.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:769)
 at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:484)
 at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:444)
 at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:708)
 at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:724)
 at org.springframework.orm.hibernate5.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:416)
 at org.springframework.orm.hibernate5.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:401)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)

Cause

The root cause is that Hibernate 5 requires jboss-logging-3.3.0.Final.jar library, while JBoss EAP packs jboss-logging-3.1.4.GA-redhat-2.jar. This error is produced when NamedQueryRepository class is trying to call the debugf(String format, Object param1) method of org.jboss.logging.Logger class.
This method exists in version 3.3.0 but not in version 3.1.4. As Jboss loads the former one, there is one incompatibiltiy.

This is the hibernate code which calls this method, org.hibernate.internal.NamedQueryRepository:


And this is the method implementation in org.jboss.logging.Logger: 


Jboss EAP 6.4 is integrated with hibernate-core-4.2.18.Final (https://access.redhat.com/articles/112673#EAP_6), which in turn required jboss-logging-3.1.0 (https://mvnrepository.com/artifact/org.hibernate/hibernate-core/4.2.18.Final)

Solution

In thease cases the solution is obvious. Just use jboss-deployment-structure and exclude Hibernate and Logging stuff in order to use all dependencies packet in the application´s lib directory. So, the first guess would be something like this:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
 <deployment>     
  <exclude-subsystems>
   <subsystem name="jpa" /> 
   <subsystem name="logging" />
  </exclude-subsystems>
  
  
  <exclusions>     
   <module name="org.hibernate"/>
   <module name="javax.persistence.api"/>
   <module name="org.hibernate.envers"/>
   <module name="org.hibernate.validator"/>
   <module name="org.hibernate.commons-annotations"/>  
   <module name="org.jboss.as.jpa.hibernate"/> 
   <module name="org.jboss.as.jpa"/> 
   <module name="org.jboss.as.jpa.util"/>  
   <module name="org.jboss.as.jpa.hibernate "/> 
   <module name="org.jboss.as.jpa.spi"/>  
   <module name="org.jboss.as.osgi.jpa"/>
   
   <module name="org.jboss.logging" /> 
   <module name="org.apache.commons.logging" />  
   <module name="org.apache.log4j" />  
   <module name="org.slf4j" />  
   <module name="org.jboss.logging.jul-to-slf4j-stub" />  
  </exclusions>
       
 </deployment>
</jboss-deployment-structure>

Unfortunately this doesn't work. There must be some kind of bug in Jboss EAP 6.4 and jboss-logging 3.1.0 is still be using. For the record, this do works with Jboss EAP 6.2.

After some digging, the solution appears to be to exclude org.jboss.resteasy.resteasy-jaxrs too. The final jboss-deployment-structure will be this:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
 <deployment>     
  <exclude-subsystems>
   <subsystem name="jpa" /> 
   <subsystem name="logging" />
  </exclude-subsystems>
  
  
  <exclusions>
   <module name="org.jboss.resteasy.resteasy-jaxrs"/>     
   <module name="org.hibernate"/>
   <module name="javax.persistence.api"/>
   <module name="org.hibernate.envers"/>
   <module name="org.hibernate.validator"/>
   <module name="org.hibernate.commons-annotations"/>  
   <module name="org.jboss.as.jpa.hibernate"/> 
   <module name="org.jboss.as.jpa"/> 
   <module name="org.jboss.as.jpa.util"/>  
   <module name="org.jboss.as.jpa.hibernate "/> 
   <module name="org.jboss.as.jpa.spi"/>  
   <module name="org.jboss.as.osgi.jpa"/>
   
   <module name="org.jboss.logging" /> 
   <module name="org.apache.commons.logging" />  
   <module name="org.apache.log4j" />  
   <module name="org.slf4j" />  
   <module name="org.jboss.logging.jul-to-slf4j-stub" />  
  </exclusions>
       
 </deployment>
</jboss-deployment-structure>

domingo, 24 de julio de 2016

Jboss - Hibernate - Infinispan cache

This is the basic configuration in order to use Hibernate Second Level Cache (2LC) inside a JBoss Server in a cluster enviroment. The cache provider will be Infinispan, wich is shipped with the server.
Second Level Cache (2LC)
A Second Level Cache (2LC) is a data store which holds persistent information relating to application state. Examples of 2LC consumers are session state replication, Single Sign On (SSO), and Java Persistence API (JPA). JBoss Enterprise Application Platform 6 uses Infinispan to manage its 2LC.
There is also a First Level Cache which is always enabled, but it works at the session level. The Hiberante Session, usually, is created when a transaction is opened and it is bounded to it. If during the life span of the transaction you get the same object two times from database, only in the first one Hibernate hits the database, the second time, the object is recovered from session.
The limitation with First Level Cache is that it only apply during the transaction, and only for the client who started it.

JBoss configuration

You need to use, at least, the HA profile (High Availability) .

Inside infinispan subsystem there should be an hibernate cache container, but it must be configured with the start attribute to EAGER.
/profile=ha/subsystem=infinispan/cache-container=hibernate:write-attribute(name=start, value=EAGER)

The result would be something like this:
<cache-container name="hibernate" start="EAGER" default-cache="local-query" module="org.jboss.as.jpa.hibernate:4">
    <transport lock-timeout="60000"/>
    <local-cache name="local-query">
        <transaction mode="NONE"/>
        <eviction strategy="LRU" max-entries="10000"/>
        <expiration max-idle="100000"/>
    </local-cache>
    <invalidation-cache name="entity" mode="SYNC">
        <transaction mode="NON_XA"/>
        <eviction strategy="LRU" max-entries="10000"/>
        <expiration max-idle="100000"/>
    </invalidation-cache>
    <replicated-cache name="timestamps" mode="ASYNC">
        <transaction mode="NONE"/>
        <eviction strategy="NONE"/>
    </replicated-cache>
</cache-container>

As you can see, there are three declared caches: local-query, entity and timestamps, each one with its own type attending to the clusterization mode. Infinispan allows the following clustering modes:
  • Local mode. Better performance, but no cluster aware.
  • Replicated mode. All the nodes in the cluster have their own local cache copy. When one node modify its local cache, it sends a message (sychronous or asynchronous) to the other nodes so they can update their local copy too.
  • Invalidation mode. No data is shared. When a node modifies some data in its own local cache copy, it sends a message to the other nodes so they can clear this object from their local copy.
  • Replication mode. Each cached object exists only in one (or in a small number) of the nodes in the cluster. Instead of replicate an object in all the nodes, they are informed about who owns it.

When the number of nodes increase (bigger than 10) the best option is the replication mode.

Application configuration

Hibernate config

Configure hibernate session factory. With the hibernateProperties you can enable 2LC and force hibernate to use the JBoss cache container obtained via JNDI.
<bean class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" id="sessionFactory" lazy-init="false">
 (...)
 <property name="hibernateProperties">
 <props>
   (...)
   
     <!-- Cache -->
     <prop key="hibernate.cache.use_second_level_cache">true</prop>
     <prop key="hibernate.cache.use_query_cache">true</prop>            
     <prop key="hibernate.cache.infinispan.statistics">true</prop>   
     <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.infinispan.JndiInfinispanRegionFactory</prop>
     <prop key="hibernate.cache.infinispan.cachemanager">java:jboss/infinispan/container/hibernate</prop> 
                               
 </props>
 </property>  
</bean>

Hibernate will use two of them, plus two other news cache wich will be configured the application startup. As you can see in org.hibernate.cache.infinispan.InfinispanRegionFactory, these caches are:
  • entity
  • collection
  • timestamps
  • query
The default values of thesea caches are well documented in this post: Using Infinispan as JPA/Hibernate Second Level Cache Provider

Hibernate will not share entities, collections or querys among the nodes of the cluster in order to reduce the traffic. For example, each node will have an entity instaced cached in its local store, and when this instance is changed by one of them, the others will be informed to evicts it.

Dependency config

You need to import the following dependencys:
<dependencyManagement>
 <dependencies>
  <dependency>
   <groupId>org.jboss.bom.eap</groupId>
   <artifactId>jboss-javaee-6.0-with-hibernate</artifactId>
   <version>6.2.0.GA</version>
   <type>pom</type>
   <scope>import</scope>
  </dependency>
 </dependencies>
</dependencyManagement>  


<dependencies>
 (...)
 
 <dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <scope>provided</scope>
 </dependency>

 <dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-infinispan</artifactId>
  <scope>provided</scope>
 </dependency>
</dependencies>

Note that I'm declaring hibernate dependencies as provided, because I will use the jars shipped inside JBoss, so the WAR does not need to have them in its lib directory.


You have to declare thease dependencies in the META-INF/jboss-deployment-structure.xml file, so JBoss will add them to the classpath during the application boot:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
 <deployment>
  <dependencies>
   <module name="org.hibernate" />
   <module name="org.javassist" />
   <module name="org.infinispan" services="import"/>   
   <module name="org.jboss.ironjacamar.jdbcadapters"/>
  </dependencies>

 </deployment>
</jboss-deployment-structure>

Documentation

Available doc:
This doc belongs to wildfly, but it can also be applied to EAP 6:

martes, 9 de diciembre de 2014

Hibernate proxy to real object - Hibernate proxy to java POJO

Objetivo

Convertir un objeto persistente, recuperado de base de datos con Hibernate, en un POJO java.
Los objetos Hibernate realmente son proxys que se utilizan, por ejemplo, para la inicialización de cargas vagas.
Cuando se tiene una lista dentro de un objeto de Hibernate, esta lista es un PersistentBag que, en el momento de acceder a ella por primera vez, accede a base de datos para cargarla.
En algunas ocasiones se necesita eliminar la "naturaleza de Hibernate" de un objeto, para quitar esos proxys y convertirlos en objetos reales. Así, a la hora de devolver un objeto en un servicio web o hacia la vista sería conveniente trabajar siempre con POJOs.

Solución

Crear un aspecto que intercepte las salidas de los DAOs y ponga a null los proxys no inicializados
En este código a los DAOs se les llama Fachada.

El aspecto inspecciona por reflexión el objeto devuelto. Revista todas sus propiedades y, si alguna no está inicializada (Hibernate.isInitialized) la setea a null.

Código fuente del aspecto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import java.lang.reflect.Field;
import java.util.Collection;

import org.aspectj.lang.ProceedingJoinPoint;
import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Aspecto que elmina la naturaleza de hibernate de un objeto. Se ejectua
 * después del retorno de las fachadas e inspecciona los objetos devueltos
 * convirtiendo los proxys de Hibernate en null.
 *
 * @author Sisifo
 *
 */
public class EliminaHibernateProxysAspect {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    public Object eliminarHibernateProxysAround(final ProceedingJoinPoint pjp) throws Throwable {
        final Object retVal = pjp.proceed();
        inicializarSiEsNecesario(retVal);
        return retVal;
    }

    @SuppressWarnings("rawtypes")
    private Object inicializarSiEsNecesario(Object object) {
        if (object == null) {
            return null;
        }

        if (!Hibernate.isInitialized(object)) {
            return null;
        } else {
            if (object instanceof Collection) {
                return inicializarColeccion((Collection) object);
            } else {
                return inicializarPropiedadesObjeto(object);
            }
        }

    }


    @SuppressWarnings("rawtypes")
    private Collection inicializarColeccion(Collection coleccion) {
        for (Object object : coleccion) {
            inicializarSiEsNecesario(object);
        }

        return coleccion;
    }

    private Object inicializarPropiedadesObjeto(Object object) {
        for (Field field : object.getClass().getDeclaredFields()) {
            if (fieldEsSusceptibleDeInicializarse(field)) {
                field.setAccessible(true);
                try {
                    field.set(object, inicializarSiEsNecesario(field.get(object)));
                } catch (final Exception e) {
                    logger.error("Se ha producido un error al acceder al campo {} del objeto {}", new Object[] { field,
                            object }, e);
                    throw new RuntimeException("Error al acceder a campo", e);
                }
            }
        }

        return object;
    }

    private boolean fieldEsSusceptibleDeInicializarse(final Field field) {
        if (Collection.class.isAssignableFrom(field.getType())) {
            return true;
        }

        if (!claseEsDeTipoBasico(field.getType()) && !clasePertenceALaEspecificiacionJava(field.getType())) {
            return true;
        }

        return false;
    }

    private boolean clasePertenceALaEspecificiacionJava(final Class<?> clase) {
        final String clasePropiedad = clase.getCanonicalName();
        if (clasePropiedad.startsWith("java.") || clasePropiedad.startsWith("javax.")) {
            return true;
        }
        return false;
    }

    private boolean claseEsDeTipoBasico(final Class<?> clase) {
        if (clase.isEnum() || clase.isPrimitive()) {
            return true;
        }
        return false;
    }
}

Fichero de cofiguración aop:


1
2
3
4
5
6
7
8
9
<bean id="eliminaHibernateProxysAspectImpl" class="es.sisifo.simplearchetype.persistencia.aop.EliminaHibernateProxysAspect"/>

<aop:config>
   <aop:pointcut id="fachadePointcut"     expression="execution(* es.sisifo..persistencia..*FacadeImpl.*(..))"/>

  <aop:aspect id="eliminaHibernateProxysAspect" ref="eliminaHibernateProxysAspectImpl">      
    <aop:around pointcut-ref="fachadePointcut" method="eliminarHibernateProxysAround"/>            
 </aop:aspect>           
</aop:config>