Thursday, May 8, 2014

JPA Criteria API Queries (CriteriaBuilder, CriteriaQuery) Create Dynamic Query instead of appending String

Here is an example about how to create a Dynamic Query.


 This is very efficient when you have a method where some parameters may be null. so instead of creating a String builder (or String Buffer) and append the SQL Query, it is better to use the Criteria API Queries.



 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
 
public List<Devices> findDevice(String enterpriseId, List<String> modelIds, List<String> deviceIds, Date constructionDate) {
 
 CriteriaBuilder builder = entityManager.getCriteriaBuilder();
 CriteriaQuery<Devices> query = builder.createQuery(Devices.class);
 Root<Devices> deviceRoot = query.from(Devices.class);

 List<Predicate> predicateList = new ArrayList<Predicate>();
 if(enterpriseId !=null ){
  predicateList.add(builder.equal(deviceRoot.get("enterprise").get("enterpriseId"), enterpriseId));

 }
 if(modelIds != null && !modelIds.isEmpty()){
  predicateList.add(builder.isTrue(deviceRoot.get("deviceModel").get("modelId").in(modelIds)));
 }
 if(deviceIds != null && !deviceIds.isEmpty()){
  predicateList.add(builder.isTrue(deviceRoot.get("deviceId").in(deviceIds)));
 }

 predicateList.add(builder.greaterThan(deviceRoot.<Date>get("constructionDate"), constructionDate));

 query.where(builder.and(predicateList.toArray(new Predicate[predicateList.size()])));

   
 return entityManager.createQuery(query).getResultList();
}

Hope it helped you :) 

Wednesday, May 7, 2014

JPA Mapping Native Query to Bean (POJO, Class) other than the Entity JPA2.1

Working with JPA 2.0, every time I need to create a new bean (named PersonDetail) from the result of a query, I create a NamedQuery like this:


1
2
3
@NamedQuery(name="findPerson", 
 query = "SELECT new  org.nd.enterprise.bean.PersonDetail(e.personId, e.birthDate)"
   + " FROM Person p WHERE p.personId = :person")

A constructor with the desired output should be set in the PersonDetail bean.

But using the NativeQuery, this is not possible as the Query will be executed like it is given.
In fact while creating the query (NativeQuery or NamedQuery), we can fore the result to be the desired Bean, but this  will generate this error:



1
2
3
4
// Here we declare a @NamedNativeQuery   
List<PersonDetail> rec = entityManager.createNamedQuery("findPerson", PersonDetail.class).getResultList();
// Here we declare just a String query = "select ...."
List<PersonDetail> rec2 = entityManager.createNativeQuery(query,PersonDetail.class).getResultList();

org.hibernate.MappingException: Unknown entity: org.nd.enterprise.bean.PersonDetail

This is normal as it accepts only an Entity and not a Class (See this post to know how to fix that with JPA2.0). Fortenately JPA2.1 comes with the solution, by adding the @ConstructorResult in the @SqlResultSetMapping where you cone construct your Class (In JPA 2.0 only the Entity cn be specified and not another POJO .)




 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 Query q = em.createNativeQuery(
      "SELECT personId, birthDate)"
   + " FROM Person p",
      "PersonDetailsResult");

   @SqlResultSetMapping(
       name="PersonDetailsResult",
       classes={
          @ConstructorResult(
               targetClass=org.nd.enterprise.bean.PersonDetail,
                 columns={
                    @ColumnResult(name="personId"),
                    @ColumnResult(name="birthDate")
                    }
          )
       }
      )

Tuesday, May 6, 2014

Resolved : Logging in Jboss EAP 6 (JBoss AS7), Spring using Log4j

I was for a good time wondering why my Log4j is logging only the messages coming from my classes but not frameworks like  Spring, JDBC Template, CSveed. Every think was well configured, no error messages but I see framework messages only in the server.log.





So finally, I found the right steps to do.

Step 1:


Exclude Logging from JBoss. This previous article explains how to do.
So here is my jboss-deployment-structure.xml :


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<jboss-deployment-structure>
 <deployment>
 <!-- Add this line in the server lauch parameter : -Dorg.jboss.as.logging.per-deployment=false -->
  <exclusions>
   <module name="org.apache.log4j"/>
   <module name="org.apache.commons.logging"/>
              <module name="org.slf4j" />
             <module name="org.slf4j.impl" />
  </exclusions>   
 </deployment>
</jboss-deployment-structure>

Step 2:


Put you log4j.xml or log4j.properties in your classpath. In a Maven project, it should be under "\src\main\resources". Othrwhise, you can fix the classpath in the web.xml



1
2
3
4
5
6
7
8
<listener>
 <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

<context-param>
 <param-name>log4jConfigLocation</param-name>
 <param-value>classpath:/main/resources/META-INF/log4j.xml</param-value>
</context-param>


Step 3:

So Now, we need to add the required jar, AND Exclude some Jars. In the pom.xml, we need to exclude commons-logging from Spring-context jar and we need to add slf4j-api, slf4j-log4j12, jcl-over-slf4j, log4j.



 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
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context</artifactId>
 <version>${spring.version}</version>
 <exclusions>
  <exclusion>
   <artifactId>commons-logging</artifactId>
   <groupId>commons-logging</groupId>
  </exclusion>
 </exclusions>
</dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-api</artifactId>
 <version>${slf4j-log4j12.version}</version>
 <scope>runtime</scope>
</dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-log4j12</artifactId>
 <version>${slf4j-log4j12.version}</version>
 <scope>runtime</scope>
</dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>jcl-over-slf4j</artifactId>
 <version>${slf4j-log4j12.version}</version>
 <scope>runtime</scope>
</dependency>
<dependency>
 <groupId>log4j</groupId>
 <artifactId>log4j</artifactId>
 <version>${log4j.version}</version>
</dependency>

So this step was the latest and Important step that I have discovered and which blocked my logging before.

Sunday, May 4, 2014

Resolve JBoss Startup Failure due to a missing deployment

On JBoss startup, I get one time this kind of error:



ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) JBAS014613: Operation ("add") failed - address: ([("deployment" => "myproject.war")]) - failure description: "JBAS018717: No deployment content with hash dcb4683c68f8ef7f07383bc7e0e0d3f039632c5f is available in the deployment content repository for deployment 'myproject.war'. This is a fatal boot error. To correct the problem, either restart with the --admin-only switch set and use the CLI to install the missing content or remove it from the configuration, or remove the deployment from the xml configuration file and restart."

In fact, Jboss tried to start but didn't find my war (myproject.war) in jbossHome/standalone/deployments.

In order to fix this error, you need to go to the standalone.xml, I have two deployments entries(app.war which don't cause error and myproject.war), and the one who is missing is myproject.war. So all what I need to do is to remove this entry.



1
2
3
4
5
6
7
8
    <deployments>
        <deployment name="app.war" runtime-name="app.war">
            <content sha1="e976bdb628576b38ac1bdee7299c55441807aa8d"/>
        </deployment>
        <deployment name="myproject.war" runtime-name="myproject.war">
            <content sha1="dcb4683c68f8ef7f07383bc7e0e0d3f039632c5f"/>
        </deployment>
    </deployments>



1
2
3
4
5
    <deployments>
        <deployment name="app.war" runtime-name="app.war">
            <content sha1="e976bdb628576b38ac1bdee7299c55441807aa8d"/>
        </deployment>
    </deployments>

Now my JBoss starts with no problems. NB : If you need to remove ALL the DEPLOYMENTS, do it but don't remove the <deployments>

Wednesday, April 30, 2014

Spring-Integration Resolve Error: one of inputChannelName or inputChannel is required

I have encountered one time this kind of error while deploying my  Web application based on Spring-Integration. The error were:

Caused by: java.lang.IllegalStateException: one of inputChannelName or inputChannel is required
at org.springframework.util.Assert.state(Assert.java:385) [spring-core-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.integration.config.ConsumerEndpointFactoryBean.initializeEndpoint(ConsumerEndpointFactoryBean.java:225) [spring-integration-core-4.0.0.M4.jar:]


In fact, I was managing errorChannel and my Service Activator responsible of handling the error messages was declared like this :


1
2
3
4
5
6
7
8
<int:channel id="errorChannel"/>

<int:service-activator id="errorHandler"
 input-channel="errorChannel" 
 method="handleError" 
 output-channel="nullChannel" 
 ref="errorHandler"/>
  

Although my input channel is declared, I get the error.
The problem was that I put the same name for the id and ref in the service-activator. So the error is caused by that and not because the input channel is not declared. So I have rectified my code :



1
2
3
4
5
6
7
8
<int:channel id="errorChannel"/>

<int:service-activator id="errorService"
   input-channel="errorChannel" 
   method="handleError" 
   output-channel="nullChannel" 
   ref="errorHandler"/>
  

Monday, April 28, 2014

Resolved : Spring Initialize a bean which depends on another bean: Use JdbcTemplate when initializing a bean.

I was working on a web project using spring. I needed in the project to initialize a cache by doing a call to the database. So in order to do so, I was putting the database call in the default class constructor.
So in my Configuration class I do

@Bean("usersCache")
@DependsOn("myJdbcTemplate")
public UsersCache userCache{
    return new UsersCache(); 
}

And in the UserCache constructor I do :

@ManagedResource
public class UsersCache {
@Autowired
JdbcTemplate jdbcTemplate;

@PostConstruct
public UsersCache(){

@SuppressWarnings({ "unchecked", "rawtypes" })
List<LatestRecordPerUser> latestRecordByDevice = jdbcTemplate.query(populateCache, new BeanPropertyRowMapper(LatestRecordPerUser.class));
for(LatestRecordPerUser record:latestRecordByDevice ){
// And Here I insert into my cache
}
}


But even using this code I get a NullPointerException as the JdbcTemplate bean is not injected:

Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [main.java.org.beans.UsersCache ]: Constructor threw exception; nested exception is java.lang.NullPointerException

I have later understand that as the UsersCache is not yet initialized, nothing will be injected to it. So only after initialization, spring will inject the jdbcTemplate and thus we can execute the request.

So the fix the problem, there is no need to use @dependsOn . We need just to populate the cache after the bean (or repository) initialisation, So this can be done thanks to @PostConstruct. So the code is now :

@Repository
@Scope("singleton")
public class UsersCache {
@Autowired
JdbcTemplate jdbcTemplate;

private final Map userCache = new ConcurrentHashMap();

@PostConstruct
public void init(){

@SuppressWarnings({ "unchecked", "rawtypes" })
List<LatestRecordPerUser> latestRecordByDevice = jdbcTemplate.query(populateCache, new BeanPropertyRowMapper(LatestRecordPerUser.class));

for(LatestRecordPerUser record:latestRecordByDevice ){
// And Here I insert into my cache
}
}

Thant's it :D

Articles les plus consultés