Spring JPA - Best way to update multiple fields - java

I'm new to using JPA and trying to transition my code from JdbcTemplate to JPA. Originally I updated a subset of my columns by taking in a map of the columns with their values and created the SQL Update string myself and executed it using a DAO. I was wondering what would be the best way to do something similar using JPA?
EDIT:
How would I transform this code from my DAO to something equivalent in JPA?
public void updateFields(String userId, Map<String, String> fields) {
StringBuilder sb = new StringBuilder();
for (Entry<String, String> entry : fields.entrySet()) {
sb.append(entry.getKey());
sb.append("='");
sb.append(StringEscapeUtils.escapeEcmaScript(entry.getValue()));
sb.append("', ");
}
String str = sb.toString();
if (str.length() > 2) {
str = str.substring(0, str.length() - 2); // remove ", "
String sql = "UPDATE users_table SET " + str + " WHERE user_id=?";
jdbcTemplate.update(sql, new Object[] { userId },
new int[] { Types.VARCHAR });
}
}

You have to read more about JPA for sure :)
Once entity is in Persistence Context it is tracked by JPA provider till the end of persistence context life or until EntityManager#detach() method is called. When transaction finishes (commit) - the state of managed entities in persistence context is synchronized with database and all changes are made.
If your entity is new, you can simply put it in the persistece context by invoking EntityManager#persist() method.
In your case (update of existing entity), you have to get a row from database and somehow change it to entity. It can be done in many ways, but the simpliest is to call EntityManager#find() method which will return managed entity. Returned object will be also put to current persistence context, so if there is an active transaction, you can change whatever property you like (not the primary key) and just finish transaction by invoking commit (or if this is container managed transaction just finish method).
update
After your comment I can see your point. I think you should redesign your app to fit JPA standards and capabilities. Anyway - if you already have a map of pairs <Attribute_name, Attrbute_value>, you can make use of something called Metamodel. Simple usage is shown below. This is naive implementation and works good only with basic attributes, you should take care of relationships etc. (access to more informations about attributes can be done via methods attr.getJavaType() or attr.getPersistentAttributeType())
Metamodel meta = entityManager.getMetamodel();
EntityType<User> user_ = meta.entity(User.class);
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaUpdate<User> update = cb.createCriteriaUpdate(User.class);
Root e = update.from(User.class);
for( Attribute<? super User, ?> attr : user_.getAttributes() ) {
if (map.containsKey(attr.getName())) {
update.set(attr, map.get(attr));
}
}
update.where(cb.equal(e.get("id"), idOfUser));
entityManager.createQuery(update).executeUpdate();
Please note that Update Criteria Queries are available in JPA since 2.1 version.
Here you can find more informations about metamodel generation.
Alternatively to metamodel you can just use java reflection mechanisms.

JPA handles the update. Retrieve a dataset as entity using the entitymanager, change the value and call persist. This will store the changed data in your db.

In case you are using Hibernate(as JPA provider), here's an example
Entity
#Entity
#Table(name="PERSON")
public class Person {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name="NAME", nullable=false)
private String name;
other fields....
}
DAO
public interface PersonDao {
Person findById(int id);
void persist(Person person);
...
}
DaoImpl
#Repository("personDao")
public class PersonDaoImpl extends AnAbstractClassWithSessionFactory implements PersonDao {
public Person findById(int id) {
return (Person) getSession().get(Person.class, id);
}
public void persist(Person person){
getSession().persist(person);
}
}
Service
#Service("personService")
#Transactional
public class PersonServiceImpl implements PersonService {
#Autowired
PersonDao personDao;
#Override
public void createAndPersist(SomeSourceObject object) {
//create Person object and populates with the source object
Person person = new Person();
person.name = object.name;
...
personDao.persist(person);
}
#Override
public Person findById(int id) {
return personDao.findById(id);
}
public void doSomethingWithPerson(Person person) {
person.setName(person.getName()+" HELLO ");
//here since we are in transaction, no need to explicitly call update/merge
//it will be updated in db as soon as the methods completed successfully
//OR
//changes will be undone if transaction failed/rolledback
}
}
JPA documentation are indeed good resource for details.
From design point of view, if you have web interfacing, i tends to say include one more service delegate layer(PersonDelegateService e.g.) which maps the actual data received from UI to person entity (and viceversa, for display, to populate the view object from person entity) and delegate to service for actual person entity processing.

Related

Update Specific Fields with Spring Data Rest and MongoDB

I'm using Spring Data MongoDB and Spring Data Rest to create a REST API which allows GET, POST, PUT and DELETE operations on my MongoDB database and it's all working fine except for the update operations (PUT). It only works if I send the full object in the request body.
For example I have the following entity:
#Document
public class User {
#Id
private String id;
private String email;
private String lastName;
private String firstName;
private String password;
...
}
To update the lastName field, I have to send all of the user object, including the password ! which is obviously very wrong.
If I only send the field to update, all the others are set to null in my database. I even tried to add a #NotNull constraints on those fields and now the update won't even happens unless I send all of the user object's fields.
I tried searching for a solution here but I only found the following post but with no solution: How to update particular field in mongo db by using MongoRepository Interface?
Is there a way to implement this ?
Spring Data Rest uses Spring Data repositories to automatically retrieve and manipulate persistent data using Rest calls (check out https://docs.spring.io/spring-data/rest/docs/current/reference/html/#reference).
When using Spring Data MongoDB, you have the MongoOperations interface which is used as a repository for your Rest endpoints.
However MongoOperations currently does not supports specific fields updates !
PS: It will be awesome if they add this feature like #DynamicUpdate in Spring Data JPA
But this doesn't mean it can be done, here's the workaround I did when I had this issue.
Firstly let me explain what we're going to do:
We will create a controller which will override all the PUT operations so that we can implement our own update method.
Inside that update method, we will use MongoTemplate which do have the ability to update specific fields.
N.B. We don't want to re-do these steps for each model in our application, so we will retrieve which model to update dynamically. In order to do that we will create a utility class. [This is optional]
Let's start by adding the org.reflections api to our project dependency which allows us to get all the classes which have a specific annotation (#Document in our case):
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.12</version>
</dependency>
Then create a new class, called UpdateUtility and add the following methods and also replace the MODEL_PACKAGE attribute with your own package containing your entities:
public class UpdateUtility {
private static final String MODEL_PACKAGE = "com.mycompany.myproject.models";
private static boolean initialized = false;
private static HashMap<String, Class> classContext = new HashMap<>();
private static void init() {
if(!initialized) {
Reflections reflections = new Reflections(MODEL_PACKAGE);
Set<Class<?>> classes = reflections.getTypesAnnotatedWith(Document.class); // Get all the classes annotated with #Document in the specified package
for(Class<?> model : classes) {
classContext.put(model.getSimpleName().toLowerCase(), model);
}
initialized = true;
}
}
public static Class getClassFromType(String type) throws Exception{
init();
if(classContext.containsKey(type)) {
return classContext.get(type);
}
else {
throw new Exception("Type " + type + " does not exists !");
}
}
}
Using this utility class we can retreive the model class to update from it's type.
E.g: UpdateUtility.getClassFromType() will returns User.class
Now let's create our controller:
public class UpdateController {
#Autowired
private MongoTemplate mongoTemplate;
#PutMapping("/{type}/{id}")
public Object update(#RequestBody HashMap<String, Object> fields,
#PathVariable(name = "type") String type,
#PathVariable(name = "id") String id) {
try {
Class classType = UpdatorUtility.getClassFromType(type); // Get the domain class from the type in the request
Query query = new Query(Criteria.where("id").is(id)); // Update the document with the given ID
Update update = new Update();
// Iterate over the send fields and add them to the update object
Iterator iterator = fields.entrySet().iterator();
while(iterator.hasNext()) {
HashMap.Entry entry = (HashMap.Entry) iterator.next();
String key = (String) entry.getKey();
Object value = entry.getValue();
update.set(key, value);
}
mongoTemplate.updateFirst(query, update, classType); // Do the update
return mongoTemplate.findById(id, classType); // Return the updated document
} catch (Exception e) {
// Handle your exception
}
}
}
Now we're able to update the specified fields without changing the calls.
So in your case, the call would be:
PUT http://MY-DOMAIN/user/MY-USER-ID { lastName: "My new last name" }
PS: You can improve it by adding the possibility to update specific field in a nested objects...

Why does not delete method of spring data (JPA) have any return values?

I have used a delete method of Spring Data JPA in the service layer, but I wonder why neither the deleteById method nor delete method has any return values.
If we inspect the implementation of the delete method carefully, there is an if statement that when the entity to be deleted doesn't exist returns nothing.
public void delete(T entity) {
Assert.notNull(entity, "Entity must not be null!");
if (entityInformation.isNew(entity)) {
return;
}
Class<?> type = ProxyUtils.getUserClass(entity);
T existing = (T) em.find(type, entityInformation.getId(entity));
// if the entity to be deleted doesn't exist, delete is a NOOP
if (existing == null) {
return;
}
em.remove(em.contains(entity) ? entity : em.merge(entity));
}
Personally, I think returning a Boolean value could be an adequate approach in this case because the controller layer will know about the deletion status, and the view layer can be provided with the far more reliable alert message.
Spring Data JPA design some build-in methods that way they think and give us the option to use the other way also.
You can easily get deleted records and their count using derived delete query supported By Spring Data JPA (Reference)
#Repository
public interface FruitRepository extends JpaRepository<Fruit, Long> {
Fruit deleteById(Long id); // To get deleted record
}
#Repository
public interface FruitRepository extends JpaRepository<Fruit, Long> {
Long deleteById(Long id); // To get deleted record count
}
use #Modifying and #Query ant it will return number of deleted rows.
#Repository
public interface FruitRepository extends JpaRepository<Fruit, Long> {
#Modifying
#Query(value = "DELETE FROM Fruit f where f.id = ?1")
int costumDeleteById(Long id);
}
Another option would be to follow the suggestion from this answer and check if the number of affected entities is 1 (in case of a deleteById method).

Using Spring HibernateTemplate. How to delete by Id?

For work with database, my class extends HibernateDaoSupport class and inside the methods I'm using Spring HibernateTemplate.
So, for delete a row in database I use this method:
public void delete(MyObject obj) {
getHibernateTemplate().delete(obj);
}
all ok!
But, at this moment I'm trying to implement a method that can delete a row based on id:
public void delete(final long id) {
// some code here
}
And I can't find some HibernateTemplate method like this:
getHibernateTemplate().remove(id)
What is a good solution for me in this case?
delete using particular id,
public void delete(long id)
{
Session session ;
MyObject myObject ;
session = sessionFactory.getCurrentSession();
myObject = (MyObject)session.load(MyObject.class,id);
session.delete(myObject);
//This makes the pending delete to be done
session.flush() ;
}
Also consider encapuslate this methods in try/catch/finally and log the error as needed
Another alternative is:
public void deleteById(Class clazz,Integer id) {
String hql = "delete " + clazz.getName() + " where id = :id";
Query q = session.createQuery(hql).setParameter("id", id);
q.executeUpdate();
}
As you mentioned, there s not such method in HibernateTemplate. You can do the following,
hibernateTemplate.delete(hibernateTemplate.get(Class,Id));
You can also use below method:
public void deleteById(Class clazz,Integer id) {
hibernateTemplate.bulkUpdate("delete from "+clazz.getName()+" where id="+id);
}
There is a simple solution by creating an object and setting only the ID:
Product product = new Product();
product.setId(37);
session.delete(product);
The drawback of this simple solution is that it doesn’t remove the associated instances.
If you have some attribute (another entity related) of the product to be deleted, you will need to load the product before.
Serializable id = new Long(17);
Object persistentInstance = session.load(Product.class, id);
if (persistentInstance != null)
{
session.delete(persistentInstance);
}
This will issue (if you have an attribute table in cascade) a delete on the children attributes.

How to enable Hibernate Interceptor when i have my Hibernate Transaction managed by Spring?

If i have a #OneToMany relationship with #Cascade(CascadeType.SAVE_UPDATE) as follows
public class One {
private Integer id;
private List<Many> manyList = new ArrayList<Many>();
#Id
#GeneratedValue
public Integer getId() {
return this.id;
}
#OneToMany
#JoinColumn(name="ONE_ID", updateable=false, nullable=false)
#Cascade(CascadeType.SAVE_UPDATE)
public List<Many> getManyList() {
return this.manyList;
}
}
And Many class
public class Many {
private Integer id;
/**
* required no-arg constructor
*/
public Many() {}
public Many(Integer uniqueId) {
this.id = uniqueId
}
/**
* Without #GeneratedValue annotation
* Hibernate will use assigned Strategy
*/
#Id
public Integer getId() {
return this.id;
}
}
If i have The following scenario
One one = new One();
/**
* generateUniqueId method will Take care of assigning unique id for each Many instance
*/
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
And i call
sessionFactory.getCurrentSession().save(one);
Before going on
According to Transitive persistence Hibernate reference documentation, you can see
If a parent is passed to save(), update() or saveOrUpdate(), all children are passed to saveOrUpdate()
ok. Now Let's see what Java Persistence With Hibernate book Talks about saveOrUpdate method
Hibernate queries the MANY table for the given id, and if it is found, Hibernate updates the row. If it is not found, insertion of a new row is required and done.
Which can be translated according to
INSERT INTO ONE (ID) VALUES (?)
/**
* I have four Many instances added To One instance
* So four select-before-saving
*
* I DO NOT NEED select-before-saving
* Because i know i have a Fresh Transient instance
*/
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
Any workaround To avoid select-before-saving ??? Yes, You can either
Add a #Version column (Not applied)
Implement isTransient method provided by Hibernate interceptor (The option i have)
So as a way to avoid select-before-saving default behavior when using this kind of cascading, i have improved my code by assigning a Hibernate Interceptor to a Hibernate Session whose Transaction is managed by Spring.
Here goes my repository
Before (Without any Hibernate Interceptor): It works fine!
#Repository
public class SomeEntityRepository extends AbstractRepository<SomeEntity, Integer> {
#Autowired
private SessionFactory sessionFactory;
#Override
public void add(SomeEntity instance) {
sessionFactory.getCurrentSession().save(instance);
}
}
After (With Hibernate Inteceptor): something goes wrong (No SQL query is performed - Neither INSERT Nor SELECT-BEFORE-SAVING)
#Repository
public class SomeEntityRepository extends AbstractRepository<SomeEntity, Integer> {
#Autowired
private SessionFactory sessionFactory;
#Override
public void add(SomeEntity instance) {
sessionFactory.openSession(new EmptyInterceptor() {
/**
* To avoid select-before-saving
*/
#Override
public Boolean isTransient(Object o) {
return true;
}
}).save(instance);
}
}
My question is: Why Spring does not persist my Entity and its relationships when using Hibernate Interceptor and what should i do as workaround to work fine ???
Spring maintains an association between the current session and the current transaction (see SessionFactoryUtils.java.) Since there is already a session associated for the current DAO method call, you have to use this Session, or take the plunge of getting involved with the murky details of associating the new session with the previous transaction context. It's probably possible, but with considerable risk, and is definitely not recommended. In hibernate, if you have a session already open, then it should be used.
Having said that, you may be able to get spring to create a new session for you and associate it with the current transaction context. Use SessionFactoryUtils.getNewSession(SessionFactory, Interceptor). If you use this rather than hibernate's sessionFactory, then this should keep the association with the transaction.
Initially, you can code this up directly in the DAO. When it's tried and tested and hopefully found to be working, you can then take steps to move the spring code out of your DAO, such as using AOP to add around advice to the add() methods that create and clean up new session.
Another alternative is to use a global Interceptor. Even though it's global, you can give it locally controllable behaviour. The TransientInterceptor contains a threadLocal<Boolean>. This is the flag for the current thread to indicate if the interceptor should return true for isTransient. You set it to true at the start of the add() method and clear it at the end. E.g.
class TransientInterceptor extends EntityInterceptor {
ThreadLocal<Boolean> transientFlag = new ThreadLocal<Boolean)();
public boolean isTransient() {
return transientFlag.get()==Boolean.TRUE;
}
static public setTransient(boolean b) {
transientFlag.set(b);
}
}
And then in your DAO:
#Override
public void add(SomeEntity instance) {
try {
TransientInterceptor.set(true);
sessionFactory.getCurrentSession().save(instance);
}
finally {
TransientInterceptor.set(false);
}
}
You can then setup TransientInterceptor as a global interceptor on the SessionFactory (e.g. LocalSessionFactoryBean.) To make this less invasive, you could create an AOP around advice to apply this behaviour to all your DAO add methods, where appropriate.
In the 'after' method you are creating a new session and not flushing it, therefore no update is sent to the database. This has nothing to do with Spring, but is pure Hibernate behavior.
What you probably want is adding an (entity) interceptor to the sessionFactory, probably configured using Spring. You can then just keep your repository's add() method as before.
See http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/orm/hibernate3/LocalSessionFactoryBean.html#setEntityInterceptor%28org.hibernate.Interceptor%29

JPA - FindByExample

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.

Categories