Using findByCriteria using Session Method in Spring Hibernate - spring

#SuppressWarnings("unchecked")
#Override
public StudentCredential getStudentDetailsByUnameAndPassword(String uname,String password){
Session session;
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(StudentCredential.class);
detachedCriteria.add(Restrictions.eq("uname", uname));
detachedCriteria.add(Restrictions.eq("password", password));
List<StudentCredential> findByCriteria = (List<StudentCredential>)Hibernate template .findByCriteria(detachedCriteria);
if(findByCriteria !=null && findByCriteria.size()>0)
return findByCriteria.get(0);
else
return null;
}
I have this code and want to use the findByCriteria of HIbernateTemplate is valdating the data NOw how can I use Session criteria query or any other method instead of Hibernate Template to make validation of email and password
This code helps to save the validate the email and password from the database.
package com.infotech.dao.impl;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate4.HibernateTemplate;
import org.springframework.stereotype.Repository;
import com.infotech.dao.StudentCredentialDao;
import com.infotech.model.Student;
import com.infotech.model.StudentCredential;
#Repository("StudentCredentialDAO")
public class StudentCredentialDaoImpl implements StudentCredentialDao {
#Autowired
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
#Override
public boolean saveStudentCred(StudentCredential stuCred) {
Session session = this.sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(stuCred);
tx.commit();
session.close();
return true;
}
NOw after saving the code uses Hibernate Template but this doesn't work for me what I can I do to validate the code
#SuppressWarnings("unchecked")
#Override
public StudentCredential getStudentDetailsByUnameAndPassword(String uname,String password){
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(StudentCredential.class);
detachedCriteria.add(Restrictions.eq("uname", uname));
detachedCriteria.add(Restrictions.eq("password", password));
List<StudentCredential> findByCriteria = (List<StudentCredential>)session.getIdentifier(detachedCriteria);
if(findByCriteria !=null && Hibernate template .size()>0)
return findByCriteria.get(0);
else
return null;
}
}
This is my full code of DAO file I don't want to use Hibernate Template it is having some problems with Oracle Database. So using session.

Related

Entity not getting saved in EclipseLink EntityListener

I have written EntityListener using eclipseLink's "DescriptorEventAdapter". I tried almost all variations whatever present online BUT the entity which I am saving from my listener is not getting saved. I suspect something fishy is going on with transaction but didn't get the root cause. Here is the code :
package com.db;
import java.util.Date;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
import org.eclipse.persistence.jpa.JpaEntityManager;
import org.eclipse.persistence.queries.InsertObjectQuery;
import org.eclipse.persistence.queries.UpdateObjectQuery;
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class NotificationMessageListener extends DescriptorEventAdapter {
public static Logger logger = LoggerFactory.getLogger(NotificationMessageListener.class);
private static final String targetColumn = "STATUS";
//private static AuditRepository auditRepo;
#Autowired
private StatusAuditRepository statusAuditRepo;
#Autowired
private RuleResultAuditRepository ruleResultRepo;
#Autowired
private EntityManagerFactory factory;
JpaEntityManager entityManager = null;
#PostConstruct
public void init() {
try {
entityManager = (JpaEntityManager) factory.createEntityManager();
// Use the entity manager to get a ClassDescriptor for the Entity class
ClassDescriptor desc =
entityManager.getSession().getClassDescriptor(NotificationMessage.class);
// Add this class as a listener to the class descriptor
desc.getEventManager().addListener(this);
} finally {
if (entityManager != null) {
// Cleanup the entity manager
entityManager.close();
}
}
}
/*#Autowired
public void setAuditRepo(AuditRepository auditRepo) {
NotificationMessageListener.auditRepo = auditRepo;
}*/
#Transactional(value = TxType.REQUIRES_NEW)
#Override
public void postInsert(DescriptorEvent event) {
logger.info("post insert is called ");
//NotificationMessage notificationMsg = (NotificationMessage) ((InsertObjectQuery) event.getQuery()).getObject();
//entityManager.getTransaction().begin();
NotificationStatusAudit statusAudit = new NotificationStatusAudit();
statusAudit.setInsertionTime(new Date());
//statusAudit.setNewVal(notificationMsg.getStatus());
statusAudit.setNewVal("abc");
statusAudit.setOldval("asdf");
statusAudit.setTargetColumnName("from listner");
//statusAudit.setTargetRecordId(notificationMsg.getId());
statusAudit.setTargetRecordId(123L);
statusAudit = statusAuditRepo.save(statusAudit);
//entityManager.getTransaction().commit();
//logger.info("Number of records "+statusAuditRepo.count());
//auditRuleResult(notificationMsg.getMessageCorrelationId() , true);
}
#Override
public void postUpdate(DescriptorEvent event) {
ObjectChangeSet objectChanges = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
DirectToFieldChangeRecord statusChanges = (DirectToFieldChangeRecord) objectChanges
.getChangesForAttributeNamed("status");
if (statusChanges != null && !statusChanges.getNewValue().equals(statusChanges.getOldValue())) {
NotificationStatusAudit statusAudit = new NotificationStatusAudit();
statusAudit.setInsertionTime(new Date());
statusAudit.setNewVal("abc");
statusAudit.setOldval("asdf");
statusAudit.setTargetColumnName(targetColumn);
statusAudit.setTargetRecordId((Long) objectChanges.getId());
statusAudit = statusAuditRepo.save(statusAudit);
}
}
}
Here all I have to do is save the record in another (Audit) table when data is getting inserted in one table. My application is spring boot app and am using eclipseLink for persistent. I had to manually register my entity-listener in "PostConstruct" because if it is registered using #EntityListner annotation , spring-data-repos were not getting autowired. Here are my questions :
1) Using EntityListener for my requirement is good approach or should I use direct "save" operations ?
2) I debugged the EntityListener code and method is not initiated a new Transaction even after adding Requires_new. I can see method is not being called $proxy (spring-proxy). I don't understand why ?
I am not sure about what you are doing in your #PostConstruct init() method... but I suspect you should be configuring this DescriptorEventAdapter using EclipseLink's DescriptorCustomizer. Here is an example:
public class MessageEventListener extends DescriptorEventAdapter implements DescriptorCustomizer {
#Override
public void customize(ClassDescriptor descriptor) {
descriptor.getEventManager().addListener(this);
}
#Override
public void postUpdate(DescriptorEvent event) {
ObjectChangeSet objectChanges = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
//More business logic...
}
}
#Entity
#Customizer(MessageEventListener.class)
public class Message {
#Id private long id;
private String content;
}

Applying custom annotation advice to spring data jpa repository

I am working on a mysql master slave replication. I am using spring data jpa(spring boot).
What I needed is all write operations to go to master server and read-only operations to be equally distributed among multiple read-only slaves.
For that I need to:
Use special JDBC driver: com.mysql.jdbc.ReplicationDriver
Set replication: in the URL:
spring:
datasource:
driverClassName: com.mysql.jdbc.ReplicationDriver
url: jdbc:mysql:replication://127.0.0.1:3306,127.0.0.1:3307/MyForum?user=root&password=password&autoReconnect=true
test-on-borrow: true
validation-query: SELECT 1
database: MYSQL
Auto commit needs to be turned off. (*)
Connection needs to be set to read-only.
To ensure JDBC Connection is set to read-only, I created an annotation and a simple AOP interceptor.
Annotation
package com.xyz.forum.replication;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by Bhupati Patel on 02/11/15.
*/
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface ReadOnlyConnection {
}
Interceptor
package com.xyz.forum.replication;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.hibernate.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.persistence.EntityManager;
/**
* Created by Bhupati Patel on 02/11/15.
*/
#Aspect
#Component
public class ConnectionInterceptor {
private Logger logger;
public ConnectionInterceptor() {
logger = LoggerFactory.getLogger(getClass());
logger.info("ConnectionInterceptor Started");
}
#Autowired
private EntityManager entityManager;
#Pointcut("#annotation(com.xyz.forum.replication.ReadOnlyConnection)")
public void inReadOnlyConnection(){}
#Around("inReadOnlyConnection()")
public Object proceed(ProceedingJoinPoint pjp) throws Throwable {
Session session = entityManager.unwrap(Session.class);
ConnectionReadOnly readOnlyWork = new ConnectionReadOnly();
try{
session.doWork(readOnlyWork);
return pjp.proceed();
} finally {
readOnlyWork.switchBack();
}
}
}
Following is my spring data repository
package com.xyz.forum.repositories;
import com.xyz.forum.entity.Topic;
import org.springframework.data.repository.Repository;
import java.util.List;
/**
* Created by Bhupati Patel on 16/04/15.
*/
public interface TopicRepository extends Repository<Topic,Integer>{
Topic save(Topic topic);
Topic findByTopicIdAndIsDeletedFalse(Integer topicId);
List<Topic> findByIsDeletedOrderByTopicOrderAsc(Boolean isDelete);
}
Following is my Manager(Service) class.
package com.xyz.forum.manager;
import com.xyz.forum.domain.entry.impl.TopicEntry;
import com.xyz.forum.domain.exception.impl.AuthException;
import com.xyz.forum.domain.exception.impl.NotFoundException;
import com.xyz.forum.entity.Topic;
import com.xyz.forum.replication.ReadOnlyConnection;
import com.xyz.forum.repositories.TopicRepository;
import com.xyz.forum.utils.converter.TopicConverter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
/**
* Created by Bhupati Patel on 16/04/15.
*/
#Repository
public class TopicManager {
#Autowired
TopicRepository topicRepository;
#Transactional
public TopicEntry save(TopicEntry topicEntry) {
Topic topic = TopicConverter.fromEntryToEntity(topicEntry);
return TopicConverter.fromEntityToEntry(topicRepository.save(topic));
}
#ReadOnlyConnection
public TopicEntry get(Integer id) {
Topic topicFromDb = topicRepository.findByTopicIdAndIsDeletedFalse(id);
if(topicFromDb == null) {
throw new NotFoundException("Invalid Id", "Topic Id [" + id + "] doesn't exist ");
}
return TopicConverter.fromEntityToEntry(topicFromDb);
}
}
In the above code #ReadOnlyConnection annotation is specified in manager or service layer. Above pieces of code works fine for me. It is a trivial case where in the service layer I am only reading from slave db and writing into master db.
Having said that my actual requirement is I should be able to use #ReadOnlyConnection in repository level itself because I have quite a few business logic where I do both read/write operation in other classes of service layer.Therefore I can't put #ReadOnlyConnection in service layer.
I should be able to use something like this
public interface TopicRepository extends Repository<Topic,Integer>{
Topic save(Topic topic);
#ReadOnlyConnection
Topic findByTopicIdAndIsDeletedFalse(Integer topicId);
#ReadOnlyConnection
List<Topic> findByIsDeletedOrderByTopicOrderAsc(Boolean isDelete);
}
Like spring's #Transactional or #Modifying or #Query annotation. Following is an example of what I am referring.
public interface AnswerRepository extends Repository<Answer,Integer> {
#Transactional
Answer save(Answer answer);
#Transactional
#Modifying
#Query("update Answer ans set ans.isDeleted = 1, ans.deletedBy = :deletedBy, ans.deletedOn = :deletedOn " +
"where ans.questionId = :questionId and ans.isDeleted = 0")
void softDeleteBulkAnswers(#Param("deletedBy") String deletedBy, #Param("deletedOn") Date deletedOn,
#Param("questionId") Integer questionId);
}
I am novice to aspectj and aop world, I tried quite a few pointcut regex in the ConnectionInterceptor but none of them worked. I have been trying this since a long time but no luck yet.
How to achieve the asked task.
I couldn't get a workaround of having my custom annotation #ReadOnlyConnection(like #Transactional) at a method level,but a small heck did work for me.
I am pasting the code snippet below.
#Aspect
#Component
#EnableAspectJAutoProxy
public class ConnectionInterceptor {
private Logger logger;
private static final String JPA_PREFIX = "findBy";
private static final String CUSTOM_PREFIX = "read";
public ConnectionInterceptor() {
logger = LoggerFactory.getLogger(getClass());
logger.info("ConnectionInterceptor Started");
}
#Autowired
private EntityManager entityManager;
#Pointcut("this(org.springframework.data.repository.Repository)")
public void inRepositoryLayer() {}
#Around("inRepositoryLayer()")
public Object proceed(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
if (StringUtils.startsWith(methodName, JPA_PREFIX) || StringUtils.startsWith(methodName, CUSTOM_PREFIX)) {
System.out.println("I'm there!" );
Session session = entityManager.unwrap(Session.class);
ConnectionReadOnly readOnlyWork = new ConnectionReadOnly();
try{
session.doWork(readOnlyWork);
return pjp.proceed();
} finally {
readOnlyWork.switchBack();
}
}
return pjp.proceed();
}
}
So in the above code I am using a pointcut like following
#Pointcut("this(org.springframework.data.repository.Repository)")
public void inRepositoryLayer() {}
and what it does is
any join point (method execution only in Spring AOP) where the proxy implements the Repository interface
You can have a look it at
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html
Now all my repository read query methods either start with a prefix "findByXXX"(default spring-data-jpa readable method) or "readXXX"(custom read method with #Query annotation) which in my around method executions matched by the above pointcut. According to my requirement I am setting the JDBC Connection readOnly true.
Session session = entityManager.unwrap(Session.class);
ConnectionReadOnly readOnlyWork = new ConnectionReadOnly();
And my ConnectionReadOnly look like following
package com.xyz.forum.replication;
import org.hibernate.jdbc.Work;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Created by Bhupati Patel on 04/11/15.
*/
public class ConnectionReadOnly implements Work {
private Connection connection;
private boolean autoCommit;
private boolean readOnly;
#Override
public void execute(Connection connection) throws SQLException {
this.connection = connection;
this.autoCommit = connection.getAutoCommit();
this.readOnly = connection.isReadOnly();
connection.setAutoCommit(false);
connection.setReadOnly(true);
}
//method to restore the connection state before intercepted
public void switchBack() throws SQLException{
connection.setAutoCommit(autoCommit);
connection.setReadOnly(readOnly);
}
}
So above settings work for my requirement.
it seems that #Pointcut && #Around should be declared in some way like follows:
#Pointcut(value = "execution(public * *(..))")
public void anyPublicMethod() {
}
#Around("#annotation(readOnlyConnection)")

Spring session on Redis - what is the failover when Redis is down

I am using Spring and Spring Security and want to use spring-session-data-redis with RedisHttpSessionConfiguration to enable storing session IDs on redis (so clients wont loose their sessions when webapp fails and switched over to another server).
My question, what happens when Redis server is down?
Will spring be able to continue to work by storing session in memory until Redis is back up? Is there a way to configure this as so?
I am using Redis on AWS ElastiCache, and Failover can take several minutes before replacement primary node is configured on the DNS.
As far as I can see, you will need to provide an implementation of CacheErrorHandler ( javadoc).
You can do this by providing a Configuration instance, that implements CachingConfigurer, and overrides the errorHandler() method.
For example:
#Configuration
#Ena1bleCaching
public class MyApp extends SpringBootServletInitializer implements CachingConfigurer {
#Override
public CacheErrorHandler errorHandler() {
return MyAppCacheErrorHandler();
}
}
Exactly HOW you will then provide uninterrupted service is not clear to me - without duplicating the current sessions in your failover cache, it seems impossible.
If you are using ElasticCache, is it not possible to have AWS handle a replicated setup for you, so that if one node goes doen, the other can take over?
I've managed to implement a fail-over mechanism to an in-memory session whenever Redis is unreachable. Unfortunately this can't be done just by a Spring property, so you have to implement your custom SessionRepository and configuring it to be used the SessionRepositoryFilter which will fail-over to the in-memory cache whenever Redis is unreachable .
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Primary;
import org.springframework.session.MapSession;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.stereotype.Component;
#Component("customSessionRepository")
#Primary
public class CustomFailoverToMapSessionRepository implements SessionRepository {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomFailoverToMapSessionRepository.class);
private GuavaBasedSessionRepository guavaBasedSessionRepository;
private SessionRepository sessionRepository;
public CustomFailoverToMapSessionRepository(SessionRepository sessionRepository, GuavaBasedSessionRepository guavaBasedSessionRepository) {
this.sessionRepository = sessionRepository;
this.guavaBasedSessionRepository = guavaBasedSessionRepository;
}
#Override
public Session createSession() {
Session session = null;
MapSession mapSession = guavaBasedSessionRepository.createSession();
try {
session = sessionRepository.createSession();
mapSession = toMapSession(session);
} catch (Exception e) {
LOGGER.warn("Unexpected exception when trying to create a session will create just an in memory session", e);
}
return session == null ? mapSession : session;
}
#Override
public void save(Session session) {
try {
if (!isOfMapSession(session)) {
sessionRepository.save(session);
}
} catch (Exception e) {
LOGGER.warn("Unexpected exception when trying to save a session with id {} will create just an in memory session", session.getId(), e);
}
guavaBasedSessionRepository.save(toMapSession(session));
}
#Override
public Session findById(String id) {
try {
return sessionRepository.findById(id);
} catch (Exception e) {
LOGGER.warn("Unexpected exception when trying to lookup a session with id {}", id, e);
return guavaBasedSessionRepository.findById(id);
}
}
#Override
public void deleteById(String id) {
try {
try {
guavaBasedSessionRepository.deleteById(id);
} catch (Exception e) {
//ignored
}
sessionRepository.deleteById(id);
} catch (Exception e) {
LOGGER.warn("Unexpected exception when trying to delete a session with id {}", id, e);
}
}
private boolean isOfMapSession(Session session) {
return session instanceof MapSession;
}
private MapSession toMapSession(Session session) {
final MapSession mapSession = guavaBasedSessionRepository.createSession();
if (session != null) {
mapSession.setId(session.getId());
mapSession.setCreationTime(session.getCreationTime());
mapSession.setLastAccessedTime(session.getLastAccessedTime());
mapSession.setMaxInactiveInterval(session.getMaxInactiveInterval());
session.getAttributeNames()
.forEach(attributeName -> mapSession.setAttribute(attributeName, session.getAttribute(attributeName)));
}
return mapSession;
}
Implement the in-memory cache session repository using Guava
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.session.MapSession;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
#Component("guavaBasedSessionRepository")
public class GuavaBasedSessionRepository implements SessionRepository<MapSession> {
private Cache<String, Session> sessionCache;
#Value("${session.local.guava.cache.maximum.size}")
private int maximumCacheSize;
#Value("${redis.session.keys.timeout}")
private long sessionTimeout;
#PostConstruct
void init(){
sessionCache = CacheBuilder
.newBuilder()
.maximumSize(maximumCacheSize)
.expireAfterWrite(sessionTimeout, TimeUnit.MINUTES)
.build();
}
#Override
public void save(MapSession session) {
if (!session.getId().equals(session.getOriginalId())) {
this.sessionCache.invalidate(session.getOriginalId());
}
this.sessionCache.put(session.getId(), new MapSession(session));
}
#Override
public MapSession findById(String id) {
Session saved = null;
try {
saved = this.sessionCache.getIfPresent(id);
} catch (Exception e){
//ignored
}
if (saved == null) {
return null;
}
if (saved.isExpired()) {
deleteById(saved.getId());
return null;
}
return new MapSession(saved);
}
#Override
public void deleteById(String id) {
this.sessionCache.invalidate(id);
}
#Override
public MapSession createSession() {
MapSession result = new MapSession();
result.setMaxInactiveInterval(Duration.ofSeconds(sessionTimeout));
return result;
}
Configure Spring to use the custom SessionRepository
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.session.Session;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.CookieHttpSessionIdResolver;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.SessionRepositoryFilter;
import javax.annotation.PostConstruct;
#EnableRedisHttpSession
#Configuration
public class CustomSessionConfig {
private CookieHttpSessionIdResolver defaultHttpSessionIdResolver = new CookieHttpSessionIdResolver();
#Autowired
private CookieSerializer cookieSerializer;
#PostConstruct
public void init(){
this.defaultHttpSessionIdResolver.setCookieSerializer(cookieSerializer);
}
#Bean
#Primary
public <S extends Session> SessionRepositoryFilter<? extends Session> sessionRepositoryFilter(CustomFailoverToMapSessionRepository customSessionRepository) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(customSessionRepository);
sessionRepositoryFilter.setHttpSessionIdResolver(this.defaultHttpSessionIdResolver);
return sessionRepositoryFilter;
}

Customizing HATEOAS link generation for entities with composite ids

I have configured a RepositoryRestResource on a PageAndSortingRepository that accesses an Entity that includes a composite Id:
#Entity
#IdClass(CustomerId.class)
public class Customer {
#Id BigInteger id;
#Id int startVersion;
...
}
public class CustomerId {
BigInteger id;
int startVersion;
...
}
#RepositoryRestResource(collectionResourceRel = "customers", path = "customers", itemResourceRel = "customers/{id}_{startVersion}")
public interface CustomerRepository extends PagingAndSortingRepository<Customer, CustomerId> {}
When i access the server at "http://<server>/api/customers/1_1" for instance, I get the correct resource back as json, but the href in the _links section for self is the wrong and also the same for any other customer i query: "http://<server>/api/customer/1"
i.e.:
{
"id" : 1,
"startVersion" : 1,
...
"firstname" : "BOB",
"_links" : {
"self" : {
"href" : "http://localhost:9081/reps/api/reps/1" <-- This should be /1_1
}
}
}
I suppose this is because of my composite Id, But I am chuffed as to how i can change this default behaviour.
I've had a look at the ResourceSupport and the ResourceProcessor class but am not sure how much i need to change in order fix this issue.
Can someone who knows spring lend me a hand?
Unfortunately, all Spring Data JPA/Rest versions up to 2.1.0.RELEASE are not able to serve your need out of the box.
The source is buried inside Spring Data Commons/JPA itself. Spring Data JPA supports only Id and EmbeddedId as identifier.
Excerpt JpaPersistentPropertyImpl:
static {
// [...]
annotations = new HashSet<Class<? extends Annotation>>();
annotations.add(Id.class);
annotations.add(EmbeddedId.class);
ID_ANNOTATIONS = annotations;
}
Spring Data Commons doesn't support the notion of combined properties. It treats every property of a class independently from each other.
Of course, you can hack Spring Data Rest. But this is cumbersome, doesn't solve the problem at its heart and reduces the flexibility of the framework.
Here's the hack. This should give you an idea how to tackle your problem.
In your configuration override repositoryExporterHandlerAdapter and return a CustomPersistentEntityResourceAssemblerArgumentResolver.
Additionally, override backendIdConverterRegistry and add CustomBackendIdConverter to the list of known id converter:
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.rest.core.projection.ProxyProjectionFactory;
import org.springframework.data.rest.webmvc.RepositoryRestHandlerAdapter;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
import org.springframework.data.rest.webmvc.support.HttpMethodHandlerMethodArgumentResolver;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.hateoas.ResourceProcessor;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.plugin.core.OrderAwarePluginRegistry;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
#Configuration
#Import(RepositoryRestMvcConfiguration.class)
#EnableSpringDataWebSupport
public class RestConfig extends RepositoryRestMvcConfiguration {
#Autowired(required = false) List<ResourceProcessor<?>> resourceProcessors = Collections.emptyList();
#Autowired
ListableBeanFactory beanFactory;
#Override
#Bean
public PluginRegistry<BackendIdConverter, Class<?>> backendIdConverterRegistry() {
List<BackendIdConverter> converters = new ArrayList<BackendIdConverter>(3);
converters.add(new CustomBackendIdConverter());
converters.add(BackendIdConverter.DefaultIdConverter.INSTANCE);
return OrderAwarePluginRegistry.create(converters);
}
#Bean
public RequestMappingHandlerAdapter repositoryExporterHandlerAdapter() {
List<HttpMessageConverter<?>> messageConverters = defaultMessageConverters();
configureHttpMessageConverters(messageConverters);
RepositoryRestHandlerAdapter handlerAdapter = new RepositoryRestHandlerAdapter(defaultMethodArgumentResolvers(),
resourceProcessors);
handlerAdapter.setMessageConverters(messageConverters);
return handlerAdapter;
}
private List<HandlerMethodArgumentResolver> defaultMethodArgumentResolvers()
{
CustomPersistentEntityResourceAssemblerArgumentResolver peraResolver = new CustomPersistentEntityResourceAssemblerArgumentResolver(
repositories(), entityLinks(), config().projectionConfiguration(), new ProxyProjectionFactory(beanFactory));
return Arrays.asList(pageableResolver(), sortResolver(), serverHttpRequestMethodArgumentResolver(),
repoRequestArgumentResolver(), persistentEntityArgumentResolver(),
resourceMetadataHandlerMethodArgumentResolver(), HttpMethodHandlerMethodArgumentResolver.INSTANCE,
peraResolver, backendIdHandlerMethodArgumentResolver());
}
}
Create CustomBackendIdConverter. This class is responsible for rendering your custom entity ids:
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
import java.io.Serializable;
public class CustomBackendIdConverter implements BackendIdConverter {
#Override
public Serializable fromRequestId(String id, Class<?> entityType) {
return id;
}
#Override
public String toRequestId(Serializable id, Class<?> entityType) {
if(entityType.equals(Customer.class)) {
Customer c = (Customer) id;
return c.getId() + "_" +c.getStartVersion();
}
return id.toString();
}
#Override
public boolean supports(Class<?> delimiter) {
return true;
}
}
CustomPersistentEntityResourceAssemblerArgumentResolver in turn should return a CustomPersistentEntityResourceAssembler:
import org.springframework.core.MethodParameter;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.core.projection.ProjectionDefinitions;
import org.springframework.data.rest.core.projection.ProjectionFactory;
import org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler;
import org.springframework.data.rest.webmvc.config.PersistentEntityResourceAssemblerArgumentResolver;
import org.springframework.data.rest.webmvc.support.PersistentEntityProjector;
import org.springframework.hateoas.EntityLinks;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
public class CustomPersistentEntityResourceAssemblerArgumentResolver extends PersistentEntityResourceAssemblerArgumentResolver {
private final Repositories repositories;
private final EntityLinks entityLinks;
private final ProjectionDefinitions projectionDefinitions;
private final ProjectionFactory projectionFactory;
public CustomPersistentEntityResourceAssemblerArgumentResolver(Repositories repositories, EntityLinks entityLinks,
ProjectionDefinitions projectionDefinitions, ProjectionFactory projectionFactory) {
super(repositories, entityLinks,projectionDefinitions,projectionFactory);
this.repositories = repositories;
this.entityLinks = entityLinks;
this.projectionDefinitions = projectionDefinitions;
this.projectionFactory = projectionFactory;
}
public boolean supportsParameter(MethodParameter parameter) {
return PersistentEntityResourceAssembler.class.isAssignableFrom(parameter.getParameterType());
}
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String projectionParameter = webRequest.getParameter(projectionDefinitions.getParameterName());
PersistentEntityProjector projector = new PersistentEntityProjector(projectionDefinitions, projectionFactory,
projectionParameter);
return new CustomPersistentEntityResourceAssembler(repositories, entityLinks, projector);
}
}
CustomPersistentEntityResourceAssembler needs to override getSelfLinkFor. As you can see entity.getIdProperty() return either id or startVersion property of your Customer class which in turn gets used to retrieve the real value with the help of a BeanWrapper. Here we are short circuit the whole framework with the use of instanceof operator. Hence your Customer class should implement Serializable for further processing.
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler;
import org.springframework.data.rest.webmvc.support.Projector;
import org.springframework.hateoas.EntityLinks;
import org.springframework.hateoas.Link;
import org.springframework.util.Assert;
public class CustomPersistentEntityResourceAssembler extends PersistentEntityResourceAssembler {
private final Repositories repositories;
private final EntityLinks entityLinks;
public CustomPersistentEntityResourceAssembler(Repositories repositories, EntityLinks entityLinks, Projector projector) {
super(repositories, entityLinks, projector);
this.repositories = repositories;
this.entityLinks = entityLinks;
}
public Link getSelfLinkFor(Object instance) {
Assert.notNull(instance, "Domain object must not be null!");
Class<? extends Object> instanceType = instance.getClass();
PersistentEntity<?, ?> entity = repositories.getPersistentEntity(instanceType);
if (entity == null) {
throw new IllegalArgumentException(String.format("Cannot create self link for %s! No persistent entity found!",
instanceType));
}
Object id;
//this is a hack for demonstration purpose. don't do this at home!
if(instance instanceof Customer) {
id = instance;
} else {
BeanWrapper<Object> wrapper = BeanWrapper.create(instance, null);
id = wrapper.getProperty(entity.getIdProperty());
}
Link resourceLink = entityLinks.linkToSingleResource(entity.getType(), id);
return new Link(resourceLink.getHref(), Link.REL_SELF);
}
}
That's it! You should see this URIs:
{
"_embedded" : {
"customers" : [ {
"name" : "test",
"_links" : {
"self" : {
"href" : "http://localhost:8080/demo/customers/1_1"
}
}
} ]
}
}
Imho, if you are working on a green field project I would suggest to ditch IdClass entirely and go with technical simple ids based on Long class. This was tested with Spring Data Rest 2.1.0.RELEASE, Spring data JPA 1.6.0.RELEASE and Spring Framework 4.0.3.RELEASE.
Although not desirable, I have worked around this issue by using an #EmbeddedId instead of a IdClass annotation on my JPA entity.
Like so:
#Entity
public class Customer {
#EmbeddedId
private CustomerId id;
...
}
public class CustomerId {
#Column(...)
BigInteger key;
#Column(...)
int startVersion;
...
}
I now see the correctly generated links 1_1 on my returned entities.
If anyone can still direct me to a solution that does not require I change the representation of my model, It would be highly appreciated. Luckily I had not progressed far in my application development for this to be of serious concern in changing, but I imagine that for others, there would be significant overhead in performing a change like this: (e.g. changing all queries that reference this model in JPQL queries).
I had a similar problem where the composite key scenarios for data rest was not working. #ksokol detailed explanation provided the necessary inputs to solve the issue. changed my pom primarily for data-rest-webmvc and data-jpa as
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-webmvc</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.7.1.RELEASE</version>
</dependency>
which solved all the issues related to composite key and I need not do the customization. Thanks ksokol for the detailed explanation.
First, create a SpringUtil to get bean from spring.
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
Then, implement BackendIdConverter.
import com.alibaba.fastjson.JSON;
import com.example.SpringUtil;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
import org.springframework.stereotype.Component;
import javax.persistence.EmbeddedId;
import javax.persistence.Id;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.net.URLEncoder;
#Component
public class CustomBackendIdConverter implements BackendIdConverter {
#Override
public boolean supports(Class<?> delimiter) {
return true;
}
#Override
public Serializable fromRequestId(String id, Class<?> entityType) {
if (id == null) {
return null;
}
//first decode url string
if (!id.contains(" ") && id.toUpperCase().contains("%7B")) {
try {
id = URLDecoder.decode(id, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
//deserialize json string to ID object
Object idObject = null;
for (Method method : entityType.getDeclaredMethods()) {
if (method.isAnnotationPresent(Id.class) || method.isAnnotationPresent(EmbeddedId.class)) {
idObject = JSON.parseObject(id, method.getGenericReturnType());
break;
}
}
//get dao class from spring
Object daoClass = null;
try {
daoClass = SpringUtil.getBean(Class.forName("com.example.db.dao." + entityType.getSimpleName() + "DAO"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//get the entity with given primary key
JpaRepository simpleJpaRepository = (JpaRepository) daoClass;
Object entity = simpleJpaRepository.findOne((Serializable) idObject);
return (Serializable) entity;
}
#Override
public String toRequestId(Serializable id, Class<?> entityType) {
if (id == null) {
return null;
}
String jsonString = JSON.toJSONString(id);
String encodedString = "";
try {
encodedString = URLEncoder.encode(jsonString, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return encodedString;
}
}
After that. you can do what you want.
There is a sample below.
If the entity has single property pk, you can use
localhost:8080/demo/1 as normal. According to my code, suppose the pk
has annotation "#Id".
If the entity has composed pk, suppose the pk is demoId type, and has
annotation "#EmbeddedId", you can use localhost:8080/demo/{demoId
json} to get/put/delete. And your self link will be the same.
The answers provides above are helpful, but if you need a more generic approach that would be following -
package com.pratham.persistence.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.istack.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import javax.persistence.EmbeddedId;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Base64;
import java.util.Optional;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Customization of how composite ids are exposed in URIs.
* The implementation will convert the Ids marked with {#link EmbeddedId} to base64 encoded json
* in order to expose them properly within URI.
*
* #author im-pratham
*/
#Component
#RequiredArgsConstructor
public class EmbeddedBackendIdConverter implements BackendIdConverter {
private final ObjectMapper objectMapper;
#Override
public Serializable fromRequestId(String id, Class<?> entityType) {
return getFieldWithEmbeddedAnnotation(entityType)
.map(Field::getType)
.map(ret -> {
try {
String decodedId = new String(Base64.getUrlDecoder().decode(id));
return (Serializable) objectMapper.readValue(decodedId, (Class) ret);
} catch (JsonProcessingException ignored) {
return null;
}
})
.orElse(id);
}
#Override
public String toRequestId(Serializable id, Class<?> entityType) {
try {
String json = objectMapper.writeValueAsString(id);
return Base64.getUrlEncoder().encodeToString(json.getBytes(UTF_8));
} catch (JsonProcessingException ignored) {
return id.toString();
}
}
#Override
public boolean supports(#NonNull Class<?> entity) {
return isEmbeddedIdAnnotationPresent(entity);
}
private boolean isEmbeddedIdAnnotationPresent(Class<?> entity) {
return getFieldWithEmbeddedAnnotation(entity)
.isPresent();
}
#NotNull
private static Optional<Field> getFieldWithEmbeddedAnnotation(Class<?> entity) {
return Arrays.stream(entity.getDeclaredFields())
.filter(method -> method.isAnnotationPresent(EmbeddedId.class))
.findFirst();
}
}

Spring QueryDSL set Session from Spring Context

I'm studying the QueryDSL library and implementing it into my DAL project.
What is clear to me is that I need to instantiate a HibernateQuery object and use the QueryDSL methods to define the source of data (from() clause) and the conditions (where() with the BooleanExpressions).
For example consider a User Entity which has a name field and suppose we want to test whether the user with a name equal to "Richie" exists into the DB. I would write the following code to make things done
public boolean richieExists()
{
QUser qUser = QUser.user;
HibernateQuery query = new HibernateQuery(session); // I need a session instance here!
User richie = query.from(qUser).where(qUser.name.eq("Richie")).uniqueResult(qUser);
return (richie!=null);
}
The problem is that the above code should be the method of a Spring's Service object which uses a Repository to execute CRUD operations. This means that I need to retrieve the Session from the EntityManager instance I'm using in the Application Context to instantiate the HibernateQuery object, and this is a problem because the Service object doesn't have a way to return the used EntityManager.
What is the right way/place to write QueryDSL queries?
Here is my DAOConfig.java class with the Spring configuration (here we define the EntityManagerFactoryBean used by Spring for the Repository operations)
package my.dal.service.dal.config;
import java.util.Properties;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#ComponentScan(basePackages = { "my.dal" })
#PropertySource("classpath:dbconnection.properties")
#EnableJpaRepositories("my.dal.repository")
#EnableTransactionManagement
public class DALConfig {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver_class";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
private static final String PROPERTY_NAME_POOL_INITIAL_SIZE = "pool.initialsize";
private static final String PROPERTY_NAME_POOL_MAX_IDLE = "pool.maxidle";
private static final String PROPERTY_NAME_DAL_CLASSES_PACKAGE = "entities.packages_to_scan";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.showsql";
private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
#Resource
private Environment environment;
#Bean
public DataSource dataSource()
{
Properties props = new Properties();
props.put("driverClassName", environment.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
props.put("url", environment.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
props.put("username", environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
props.put("password", environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
props.put("initialSize", environment.getRequiredProperty(PROPERTY_NAME_POOL_INITIAL_SIZE));
props.put("maxIdle", environment.getRequiredProperty(PROPERTY_NAME_POOL_MAX_IDLE));
BasicDataSource bds = null;
try {
bds = BasicDataSourceFactory.createDataSource(props);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return bds;
}
#Bean
public PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor()
{
PersistenceExceptionTranslationPostProcessor b = new PersistenceExceptionTranslationPostProcessor();
return b;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator(){
return new HibernateExceptionTranslator();
}
#Bean
public PlatformTransactionManager transactionManager() throws ClassNotFoundException {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws ClassNotFoundException {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPackagesToScan(environment.getRequiredProperty(PROPERTY_NAME_DAL_CLASSES_PACKAGE));
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
Properties jpaProperties = new Properties();
jpaProperties.put(PROPERTY_NAME_HIBERNATE_DIALECT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
jpaProperties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
jpaProperties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
}
This is my repository interface
package my.dal.repository;
import my.domain.dal.User;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface IUserRepository extends CrudRepository<User, String>{
}
This is the UserService Service class in which I have to implement the "richieExists" query method
package my.dal.service;
import my.dal.repository.IUserRepository;
import my.domain.dal.QUser;
import my.domain.dal.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import com.mysema.query.jpa.hibernate.HibernateQuery;
#Service
public class UserService {
#Autowired
private IUserRepository repository;
public User find(String username) throws DataRetrievalFailureException
{
User user = null;
user= repository.findOne(username);
if (user == null)
throw new DataRetrievalFailureException("User with username = \"" + username + "\" not found");
else
return user;
}
public User insert(User user) throws DuplicateKeyException
{
if (repository.findOne(user.getUsername()) != null)
throw new DuplicateKeyException("User with username = \"" + user.getUsername() + "\" already exists");
return repository.save(user);
}
public void delete(String username) throws DataRetrievalFailureException
{
if (repository.findOne(username) == null)
throw new DataRetrievalFailureException("User with username =\"" + username + "\" not found");
repository.delete(username);
}
public User update(User user) throws DataRetrievalFailureException
{
if (repository.findOne(user.getUsername()) == null)
throw new DataRetrievalFailureException("User with username = \"" + user.getUsername() + "\" not found");
return repository.save(user);
}
public boolean richieExists()
{
QUser qUser = QUser.user;
HibernateQuery query = new HibernateQuery(session); // I need a session instance here!
User richie = query.from(qUser).where(qUser.username.eq("richie")).uniqueResult(qUser);
return (richie!=null);
}
}
Thank you
I solved this way:
Got the current EntityManager from the persistence context by mean of the proper annotation
#PersistenceContext
EntityManager em;
Next I used the JPAQuery class (not the HibernateQuery one) to build the queries
JPQLQuery query = new JPAQuery(em); // Now just use the query object
Hope this help

Resources