In Hibernate, is there a way to create an add an Alias to a Criterion object. I have the following to work with:
I have a dynamic search from big Database with many tables. The search has many (25+) optional non-exclusive parameters selected clien-side. This requires the use of the Hibernate Criteria API for managability. In my DAO I have the following method:
Public List<myPojoClass> getDataByCriterion( List<Criterion> restrictionList) {
Session s = HibernateUtil.currentSession();
Criteria c = s.createCriteria(myPojo.class);
for (Criterion crit : restrictionList){
c.add(crit);
}
List<myPojoClass> response = c.list();
return response;
}
I need to do a Join with myOtherPojo.class and would like to know if it is possible to add an alias to the Criteria list above.
Somthing like :
restrictionsList.add(... ...createAlias("myOtherPojo.class" , "mop");
then, I need o add other Logical and to this class as above.
You again! ;)
You could pass a collection of entries (like a HashMap<String, String>) and iterate over them to populate your aliases... like this:
Public List<myPojoClass> getDataByCriterion( List<Criterion> restrictionList, HashMap<String,String> aliases) {
Session s = HibernateUtil.currentSession();
Criteria c = s.createCriteria(myPojo.class);
for (Criterion crit : restrictionList){
c.add(crit);
}
for (Entry<String, String> entry : aliases.entrySet()){
c.createAlias(entry.getKey(), entry.getValue());
}
List<myPojoClass> response = c.list();
return response;
}
You could do something similar if you want to change the fetch modes. Of course the calling method needs to know the data model so it can set up the aliases properly, otherwise you can get errors at runtime.
from what I know there is now way to create joins without instance of Criteria. I suggest you create some wrapper for criteria which would contain criteria and alias definition if necessary and then use Criteria as visitor (like from this Pattern)
interface CriterionWrapper {
void visit(Criteria c);
}
class OnlyCriterionWrapper implements CriterionWrapper {
private Criterion c;
public void visit(Criteria c){c.add(c);}
}
class CriterionWrapper implements CriterionWrapper{
private Criterion c;
private String whateverIsNeededToCreateAlias
public void visit(Criteria c){
c.createAlias(whateverIsNeededToCreateAlias);
c.add(c);
}
}
and then pass List as parameter to your getDataByCriterion() method
Related
The task is to retrieve data from the database for certain list of columns and return as custom already existing class.
I tried to resolve this task with following code:
public List<EntityOne> getServiceProviders(EntityTwo EntityTwo) {
Criteria criteria = createCriteria(EntityOne.class);
criteria.add(Restrictions.eq("EntityTwo", EntityTwo));
criteria.createAlias("spid", "two");
criteria.addOrder(Order.asc("two.entityName"));
criteria.setProjection(Projections.projectionList()
.add(Projections.property("entityId"), "entityId")
.add(Projections.property("publishStatus"), "publishStatus")
.add(Projections.property("two.entityName"), "two.entityName")
.add(Projections.property("two.entityId"), "two.entityId")
);
return criteria.list();
}
But I receive a list of data that is not grouped in class as I wanted.
Your question is not very clear especially where you are attempting to use Restrictions.eq("EntityTwo", EntityTwo) as this will not give proper results. Hibernate however provides a means to return EntityOne as an Object from the Columns selected using Hibernate Transformers class. In your case, you will need to write a custom class with getter setter of the columns you are returning. Note that is is important that the variables be named exactly like the alias columns.
Since your example is not clear, let me illustrate with a simple example: Say I need all purchase OrderAmount grouped by OrderDate and OrderNumber
public static List<YourCustomEntity> getAggregateOrders(){
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = null;
List<YourCustomEntity> list = new ArrayList<YourCustomEntity>();
try {
tx = session.beginTransaction();
Criteria cr = session.createCriteria(PurchaseOrders.class);
cr.setProjection(Projections.projectionList()
.add(Projections.sum("orderAmount").as("sumOrderAmount"))
.add(Projections.groupProperty("orderNumber").as("agOrderNumber"))
.add(Projections.groupProperty("orderDate").as("agOrderDate")));
cr.setResultTransformer(Transformers.aliasToBean(YourCustomEntity.class));
list = (List<YourCustomEntity>) cr.list();
}catch (Exception asd) {
System.out.println(asd.getMessage());
if (tx != null) {
tx.rollback();
}
} finally {
session.close();
}
return list;
}
In this regard, you will need your customEntity to have the three columns returned above. For example:
public class YourCustomEntity {
private double sumOrderAmount;
private String agOrderNumber;
private Date agOrderDate;
//And then the getters and setters
NB: Notice the naming of the variables is the same as the column aliases.
You should use Projections.groupProperty(propertyName);
criteria.setProjection(Projections.groupProperty("propertyName"));
So when we use JDBI to query from database, it is getting it into a Map<String, Object> type.
I want to get it as my customized object (constructor) instead of Map<String, Object>.
DBI dbi = establishConnection(url, userName, passWord);
Handle handle = dbi.open();
List<Map<String, Object>> rs = handle.select("select * from sometable");
Instead I want to use:
List<customizedObject> rs = handle.select("select * from sometable");
Where customizedObject class is an object that contains all the column properties with it.
Is there any way to do this? I found some relative documentation, but I cannot really understand the implementation.
http://jdbi.org/sql_object_api_queries/
Please also see the previous page in the documentation that shows how to link your Handle or DBI with the mappers.
Essentially, you need a mapper to convert the ResultSet to the desired object and an interface to refer to the mapper.
Let's assume a minimal example. First the mapper needs to be provided:
public class CustomizedObjectMapper implements ResultSetMapper<customizedObject> {
#Override
public customizedObject map(int index, ResultSet r, StatementContext ctx)
throws SQLException {
return new customizedObject(r.getString("uuid"), r.getString("other_column"));
}
}
Then we need an interface to define which query provides the data that is passed to the mapper class. One result row leads to one invocation of CustomizedObjectMapper.map(...):
#RegisterMapper(CustomizeObjectMapper.class)
public interface CustomizeObjectQuery {
#SqlQuery("Select uuid, other_column from schema.relation")
List<customizedObject> get();
}
Finally, the objects can be retrieved: List<customizedObject> test = dbi.open(CustomizeObjectQuery.class).get().
Your can also put the components together on an individual basis like so and omit the interface:
dbi.open().createQuery("Select uuid, other_colum from schema.relation").map(new EventMapper()).list()
I am working on an object cache of CMS objects. I need to maintain a parent/child relationship between a Product and child objects (Options and Attributes). The parent (Product) is illustrated in the first code sample.
It is easy enough to do, but I am looking for a way to make the assignment of the child to the parent, as shown in the 2nd code block, generic.
Since all CMS objects extend CMSContent, I can use ProductID. However, is there a way to make the field (e.g. ProductAttribute) generic so that I can put the algorithm in a method and call the method with a parent and child object to make the attribute assignment?
I know that an ORM framework like Hibernate is appropriate here, but that won't fit since I have a fixed database structure.
public class Product extends CMSContent {
private List<ProductAttribute> productAttributes;
private List<ProductOptions> productOptions;
// getters,setters
}
Algorithm to match them up.
// attach Product Attributes to Product
for (Product p : listP) {
Map<String, Object> parameters = new HashMap<String, Object>();
for (ProductAttribute po : listPA) {
parameters.put("pid", p.getPid());
parameters.put("ref", po.getRid());
int i = jdbcTemplate.queryForInt(sqlAttr, parameters); // select count(*), 1 if matched.
if (i == 1) {
p.getProductAttributes().add(po); // generic field?
}
}
}
Wouldn't this two Methods in Product help
public void add(ProductAttribute item){
productAttributes.add(item);
}
public void add(ProductOption item){
productOption.add(item);
}
so you should be able to just add a ProductAttribute or a ProductOption
I love the type safety CriteriaQuery brings ing JPA 2.0 but it also brings a bit of boiler-plate code. For example, let say I have an entity called NamedEntity, which simply has an id and a String field called "name" (assume it has the unique constraint set to true). Here's what the NamedEntityManager might look like:
public class NamedEntityManager
{
//inject using your framework
EntityManager entityManager;
//retrieve all existing entities of type NamedEntity from DB
public Iterable<NamedEntity> queryAll()
{
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<NamedEntity> query = builder.createQuery(NamedEntity.class);
return entityManager.createQuery(query).getResultList();
}
//retrieve a single entity of type NamedEntity from DB using specified name
public NamedEntity queryByName(String name)
{
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<NamedEntity> query = builder.createQuery(NamedEntity.class);
Root<NamedEntity> root = query.from(NamedEntity.class);
query = query.where(root.<NamedEntity>get("name").in(name));
//skipped the try/catch block for the sake of brevity
return entityManager.createQuery(query).getSingleResult();
}
}
Is there a way to condense the code in order to avoid copying/pasting the same lines of code into each query method? Perhaps somehow reuse the CriteriaQuery object?
I was looking for something like that, you could take a look at Querydsl (LGPL licensed) which can have JPA as backend.
Im still reading into it, but from their examples, it looks pretty clean.
HQLQuery q = new HibernateQuery(session);
QCat cat = new QCat("cat"); // query type
List<Cat> cats = q.from(cat).where(cat.name.between("A", "B")).list(cat);
In JPA 2.1, it will most probably be possible to mix JPQL and Criterias. With such an approach you could define a base query with JPQL and then use the Criteria API to dynamically add small parts.
I figure the API will be less verbose then, since you only need to use small parts of it.
Then Use JPA-2.0 MetaData model.
http://docs.jboss.org/hibernate/jpamodelgen/1.0/reference/en-US/html_single/
It seems there's no way to reduce the amount of code. I guess something had to be sacrificed to gain type safety.
Way outdated, this post, but I want to add what I recently built for simple queries
public static class Jpa2Whatsoever {
private final EntityManager em;
public class Jpa2WhatsoeverProgress<T> {
private CriteriaQuery<T> cq;
private List<Predicate> predicates = new ArrayList<>();
private Root<T> root;
public Jpa2WhatsoeverProgress(Class<T> type) {
this.cq = em.getCriteriaBuilder().createQuery(type);
this.root = cq.from(type);
}
public Jpa2WhatsoeverProgress<T> where(String attributeName, Object value) {
Predicate equal = em.getCriteriaBuilder().equal(root.get(attributeName), value);
predicates.add(equal);
return this;
}
public List<T> getResultList() {
Predicate[] predicatesArray = new Predicate[predicates.size()];
TypedQuery<T> typedQuery = em.createQuery(cq.select(root).where(predicates.toArray(predicatesArray)));
List<T> resultList = typedQuery.getResultList();
return Collections.unmodifiableList(resultList);
}
}
public Jpa2Whatsoever(EntityManager entityManager) {
this.em = entityManager;
}
public <T> Jpa2WhatsoeverProgress<T> select(Class<T> type) {
return new Jpa2WhatsoeverProgress<T>(type);
}
}
You can use it like this
List<MyEntity> matchingEntities = new Jpa2Whatsoever(entityManager).select(MyEntity.class).where("id", id).where("due", new Date()).getResultList();
In the end I stopped this. Mainly because I saw that I had only two queries and I would have to extend the DSL to get the required query characteristics into it, such as
greater than, less than
Metamodel support
QueryBuilder.currentDate() and alike.
Further, I find it ugly to always call where while it actually corresponds to a more SQLly and. Anyway, if someone is interested in a very simple query API, it is still worth a try.
BTW: Forget about the names, this was a prototype, nothing more.
Does anyone have a good example for how to do a findByExample in JPA that will work within a generic DAO via reflection for any entity type? I know I can do it via my provider (Hibernate), but I don't want to break with neutrality...
Seems like the criteria API might be the way to go....but I am not sure how to handle the reflection part of it.
Actually, Query By Example (QBE) has been considered for inclusion in the JPA 2.0 specification but is not included, even if major vendors support it. Quoting Mike Keith:
I'm sorry to say that we didn't actually get to do QBE in JPA 2.0. Criteria API does not have any special operators for it so entity equality is just like in JP QL, based on PK value. Sorry, but hopefully we'll be more successful on that front in the next go-round. For now it is one of those vendor features that every vendor supports, but is not in the spec yet.
Just in case, I've added (non generic) sample code for the major vendors below for documentation purposes.
EclipseLink
Here is a sample of using QBE in the EclipseLink JPA 2.0 reference implementation:
// Create a native EclipseLink query using QBE policy
QueryByExamplePolicy policy = new QueryByExamplePolicy();
policy.excludeDefaultPrimitiveValues();
ReadObjectQuery q = new ReadObjectQuery(sampleEmployee, policy);
// Wrap the native query in a standard JPA Query and execute it
Query query = JpaHelper.createQuery(q, em);
return query.getSingleResult();
OpenJPA
OpenJPA supports this style of query through its extended OpenJPAQueryBuilder interface:
CriteriaQuery<Employee> q = cb.createQuery(Employee.class);
Employee example = new Employee();
example.setSalary(10000);
example.setRating(1);
q.where(cb.qbe(q.from(Employee.class), example);
Hibernate
And with Hibernate's Criteria API:
// get the native hibernate session
Session session = (Session) getEntityManager().getDelegate();
// create an example from our customer, exclude all zero valued numeric properties
Example customerExample = Example.create(customer).excludeZeroes();
// create criteria based on the customer example
Criteria criteria = session.createCriteria(Customer.class).add(customerExample);
// perform the query
criteria.list();
Now, while it should be possible to implement something approaching in a vendor neutral way with JPA 2.0 Criteria API and reflection, I really wonder if it's worth the effort. I mean, if you make any of the above snippets generic and put the code in a DAO method, it would be quite easy to switch from one vendor to another if the need should arise. I agree it's not ideal, but still.
References
What about findByExample in JPA book?
Dynamic, typesafe queries in JPA 2.0
This is quite crude and i'm not convinced it's a good idea in the first place. But anyway, let's try to implement QBE with the JPA-2.0 criteria API.
Start with defining an interface Persistable:
public interface Persistable {
public <T extends Persistable> Class<T> getPersistableClass();
}
The getPersistableClass() method is in there because the DAO will need the class, and i couldn't find a better way to say T.getClass() later on. Your model classes will implement Persistable:
public class Foo implements Persistable {
private String name;
private Integer payload;
#SuppressWarnings("unchecked")
#Override
public <T extends Persistable> Class<T> getPersistableClass() {
return (Class<T>) getClass();
}
}
Then your DAO can have a findByExample(Persistable example) method (EDITED):
public class CustomDao {
#PersistenceContext
private EntityManager em;
public <T extends Persistable> List<T> findByExample(T example) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
Class<T> clazz = example.getPersistableClass();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T> cq = cb.createQuery(clazz);
Root<T> r = cq.from(clazz);
Predicate p = cb.conjunction();
Metamodel mm = em.getMetamodel();
EntityType<T> et = mm.entity(clazz);
Set<Attribute<? super T, ?>> attrs = et.getAttributes();
for (Attribute<? super T, ?> a: attrs) {
String name = a.getName();
String javaName = a.getJavaMember().getName();
String getter = "get" + javaName.substring(0,1).toUpperCase() + javaName.substring(1);
Method m = cl.getMethod(getter, (Class<?>[]) null);
if (m.invoke(example, (Object[]) null) != null)
p = cb.and(p, cb.equal(r.get(name), m.invoke(example, (Object[]) null)));
}
cq.select(r).where(p);
TypedQuery<T> query = em.createQuery(cq);
return query.getResultList();
}
This is quite ugly. It assumes getter methods can be derived from field names (this is probably safe, as example should be a Java Bean), does string manipulation in the loop, and might throw a bunch of exceptions. Most of the clunkiness in this method revolves around the fact that we're reinventing the wheel. Maybe there's a better way to reinvent the wheel, but maybe that's where we should concede defeat and resort to one of the methods listed by Pascal above. For Hibernate, this would simplify the Interface to:
public interface Persistable {}
and the DAO method loses almost all of its weight and clunkiness:
#SuppressWarnings("unchecked")
public <T extends Persistable> List<T> findByExample(T example) {
Session session = (Session) em.getDelegate();
Example ex = Example.create(example);
Criteria c = session.createCriteria(example.getClass()).add(ex);
return c.list();
}
EDIT: Then the following test should succeed:
#Test
#Transactional
public void testFindFoo() {
em.persist(new Foo("one",1));
em.persist(new Foo("two",2));
Foo foo = new Foo();
foo.setName("one");
List<Foo> l = dao.findByExample(foo);
Assert.assertNotNull(l);
Assert.assertEquals(1, l.size());
Foo bar = l.get(0);
Assert.assertNotNull(bar);
Assert.assertEquals(Integer.valueOf(1), bar.getPayload());
}
You should check the solution proposed by Springfuse using Spring Data & JPA 2.
http://www.springfuse.com/2012/01/31/query-by-example-spring-data-jpa.html
Some sample source code here (under repository sub package):
https://github.com/jaxio/generated-projects
Found this project: https://github.com/jaxio/jpa-query-by-example
https://github.com/superbiger/sbiger-jpa-qbe
I'think query by example with single table like mybatis is easy to use
base on jpa we can also support Join/GroupBy like this:
/*
SQL:
select * from
user
where
id=1
or id=2
group by
id,
name
order by
id asc,
name asc
limit ?
*/
public List<User> findAll(){
Example<User> example = ExampleBuilder.create();
example.or()
.andEqual("id", 1)
.orEqual("id", 2);
example.groupBy("id","name");
example.asc("id","name");
return userReponsitory.findAll(example, new PageRequest(0, 1));
}
Features now:
Support and/or logic operation
Support is(Empty/Boolean/Null)
Support Equal/NotEqual/In/NotIn/Like/NotLike
Support gt/ge/lt/le/between
Support join query
Support group by
Support custom specification.
Support pagination
more features coming soon……
Criteria API is your best bet. You'll need a JPA-2.0 provider for that, though. So if you have an entity like this:
#Entity
public class Foo {
#Size(max = 20)
private String name;
}
The following unit test should succeed (i tested it with EclipseLink, but it should work with any of the JPA-2.0 providers):
#PersistenceContext
private EntityManager em;
#Test
#Transactional
public void testFoo(){
Foo foo = new Foo();
foo.setName("one");
em.persist(foo);
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Foo> c = cb.createQuery(Foo.class);
Root<Foo> f = c.from(Foo.class);
c.select(f).where(cb.equal(f.get("name"), "one"));
TypedQuery<Foo> query = em.createQuery(c);
Foo bar = query.getSingleResult();
Assert.assertEquals("one", bar.getName());
}
Also, you might want to follow the link to the tutorial referenced here.
you can use this https://github.com/xiaod0510/jpa-findbyexample
if your entity is Contact:
#Entity
public class Contact {
#Id
#GeneratedValue
private Long id;
#Column
private String name;
#Column
private Date birthday;
//Getter and Setter
}
public interface ContactRepository
extends
JpaSpecificationExecutor<Contact> {
}
just create your own Example like this:
public class ContactExample extends BaseExample<ContactExample, Contact> {
public final Attr<Long> id = new Attr<Long>("id");
public final Attr<String> name = new Attr<String>("name");
public final Attr<Date> birthday = new Attr<Date>("birthday");
//default builder
public static ContactExample where() {
ContactExample example = new ContactExample();
example.operatorType = OperatorType.and;
return example;
}
}
and now you can query by example :
ContactRepository.findOne(ContactExample
.where()//default is and
.id.eq(1l)
);
the example implements the interface "Specification",more information on that github
Maybe the answer is too late. But check this. It might be of help.
https://sourceforge.net/projects/simplejpaquery/
First, include the jar into the classpath. You will have a class called com.afifi.simpleJPAQuery.entities.utility.JPAUtil.
This class uses reflection to deduct the query from the bean.
Suppose you have an entity bean as follows:
#Entity
public class Person {
#Id
private Integer personNo;
private String personName;
public Integer getPersonNo() {
return personNo;
}
public void setPersonNo(Integer personNo) {
this.personNo = personNo;
}
public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
}
}
Then if you want to query by person name for instance, you need to do as follows:
//initiate entity manager (em)
Person p=new Person();
p.setPersonName("John");
String sortString="";
List<Person> result= JPAUtil.findByExample(em,p,sortString);
The result will get all the records where the person name contained the word "John".
if you want to limit the results, you can do something like:
List<Person> result= JPAUtil.findByExample(em, p, sortString, start, size);
This library has other methods like:
getResultCount: to get the count of the result
createSqlStatement: to get the sql statement that is being used
getSqlWhereString: to get just the where string used
It has the native forms of these functions:
findByExampleNative, getResultCountNative, createSqlStatementNative and getSqlWhereStringNative
The library also has QueryAnnotations class that contains annotations that can be added to the Entity bean properties to give more control on how you want to query using the bean.