Using Spring JPA Projection with JHipster - spring

I'm having some difficulties to use Spring Data JPA Projections in a project generated with JHipster version 4.14.5.
I'm following Spring's orientation on how to make projections using JPA respositories, however I'm not having any success. The repo give me null values when I try to use projections.
Since I'm not a big connoisseur of JHipster's pipes, I hope somebody out there may help me out.
My Entity
#Entity
#Table(name = "research")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Research implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Column(name = "answer", nullable = false)
private String answer;
#OneToOne(optional = false)
#NotNull
#JoinColumn()
private Question question;
// getters and setters
Entity projection:
public interface ResearchSimple {
Long getId();
String getAnswer();
}
Entity's repo:
#Repository
public interface ResearchRepository
extends JpaRepository<Research, Long> {
#Query("SELECT r FROM Research r)
List<ResearchSimple> findAllAsSimple();
}
Test Results
List<ResearchSimple> result = repo.findAllAsSimple();
assertEquals(result.size, dbSize); // OK
ResearchSimple simple = result.get(0);
assertNotNull(simple); // OK
assertNotNull(simple.getId); // FAIL!
assertNotNull(simple.getAnswer); // FAIL!
Debugging the value obtained in simple I've noticed that the projection was made, but I can't access it's value. Notice that the class br.com.pixinside.projection.ResearchSimple is present in simple's advised.
org.springframework.aop.framework.ProxyFactory: 2 interfaces [br.com.pixinside.projection.ResearchSimple, org.springframework.data.projection.TargetAware]; 3 advisors [org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor#1d62a0b], org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [org.springframework.data.projection.ProxyProjectionFactory$TargetAwareMethodInterceptor#3b89f41a], org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [org.springframework.data.projection.ProjectingMethodInterceptor#198453c9]]; targetSource [SingletonTargetSource for target object [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap#319267ad]]; proxyTargetClass=false; optimize=false; opaque=true; exposeProxy=false; frozen=false

if you wanna use projection with manually queries , then you should use aliases that matches field names in your projection interface. #Query("SELECT r.id as id, r.answer as answer FROM Research r)
Or just skip #Query, and use List<ResearchSimple> findAllSimplifiedBy();

Related

Get entity property with Spring JPA

I'm using Spring JPA in my DAO layer. I have an entity Projet having inside an entity property Client:
Project.java
#Entity
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int projetId;
private String libelle;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="client_id")
private Client client;
// ... constructors, getters & setters
}
Client.java
#Entity
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int clientId;
private String denomination;
// ... constructors, getters & setters
}
in my DAO interface I have the following specifications:
ProjetDao.java
#Repository
#Transactional
public interface ProjetDao extends CrudRepository<Projet, Integer> {
#Transactional
public Projet findByLibelle(String libelle);
#Transactional
public Projet findByProjetId(int projetId);
}
My question is: How can I specify in my DAO interface a method that will return all clients distinct in List<Client>?
From the documentation and JIRA:
List<Project> findAllDistinctBy();
The query builder mechanism built into Spring Data repository infrastructure is useful for building constraining queries over entities of the repository. The mechanism strips the prefixes find…By, read…By, query…By, count…By, and get…By from the method and starts parsing the rest of it. The introducing clause can contain further expressions such as a Distinct to set a distinct flag on the query to be created. However, the first By acts as delimiter to indicate the start of the actual criteria. At a very basic level you can define conditions on entity properties and concatenate them with And and Or.
You are dealing with a one-to-one relationship, in this case I guess the list that you need is not really related to specific project, you just want a distinct list of clients.
You will need to create another repository (ClientRepository) for the Client entity and add a findAllDistinct method in this repository.

spring jpa projection nested bean

is it possible to have a projection with nested collection with Spring JPA?
I have the following 2 simple entity (to explain the problem)
#Entity
#Table(name = "person")
public class Person implements Serializable {
private Integer id;
private String name;
#OneToMany
private List<Address> addressList = new ArrayList<>();
}
#Entity
#Table(name = "address")
public class Address implements Serializable {
private Integer id;
private String city;
private String street;
}
Is it possible to have a projection of Person with following attributes filled in ? {person.name, address.city}
I might be wrong in semantics of word Projection. but the problem is what i need to achieve. Maybe it is not possible with Projection, but is there another way to achieve the end goal? Named Entity graph perhaps ?
P.S. please suggest a solution for Spring JPA not Spring Jpa REST
thanks in advance
You're right, Entity Graphs serve this exact purpose - control field loading.
Create entity graphs dynamically from the code or annotate target entities with Named Entity Graphs and then just use their name.
Here is how to modify your Person class to use Named Entity Graphs:
#Entity
#Table(name = "person")
#NamedEntityGraph(name = "persion.name.with.city",
attributeNodes = #NamedAttributeNode(value = "addressList", subgraph = "addresses.city"),
subgraphs = #NamedSubgraph(name = "addresses.city", attributeNodes = #NamedAttributeNode("city")))
public class Person implements Serializable {
private Integer id;
private String name;
#OneToMany
private List<Address> addressList;
}
And then when loading your person:
EntityGraph graph = em.getEntityGraph("person.name.with.city");
Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);
return em.find(Person.class, personId, hints);
The same applies for queries, not only em.find method.
Look this tutorial for more details.
I think that that's not usual scenario of Data JPA usage. But you can achieve your goal with pure JPQL:
SELECT a.street, a.person.name FROM Address a WHERE …
This solution has 2 drawbacks:
It forces you to have bidirectional relationship Address ←→ Person
It returns List
Another solution (and that's preferred JPA way) is to create DTO like this:
class MyPersonDTO {
private String personName;
private List<String> cities;
public MyPersonDTO(String personName, List<Address> adresses) {
this.personName = personName;
cities = adresses
.stream()
.map(Address::getCity)
.collect(Collectors.toList());
}
}
And the execute JPQL query like this:
SELECT NEW package.MyPersonDTO(p.name, p.addressList) FROM Person p WHERE …
Return type will be List<MyPersonDTO> in that case.
Of course you can use any of this solutions inside #Query annotation and it should work.

Spring JPA one to many denormalized count field

I have two entities, Books and Comments, in a one to many relationship (one book can have many comments). I want to be able to list books and number of comments about a book. I want it denormalized, meaning the books entity will have a counter that has number of comments for that book, and it will be updated every time a comment is entered (just playing with the concept, no need to discuss about the need of denormalizing here).
I think (correct me if I am wrong) this could be easily done with a trigger in the database (whenever a new comment is created, update a counter in the books table to the corresponding bookId), but for the sake of learning I want to do it through JPA, if it makes sense.
What I have so far: //omitted some annotations, just general info
Boks entity:
#Entity
public class Books {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String author;
private Long numComments;
// getters and setters...
}
Comments entity:
#Entity
public class Comments {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String comment;
private Long authorId;
private Long bookId;
// getters and setters...
}
Books repository: I added here a query to perform the update
/**
* Spring Data JPA repository for the Books entity.
*/
public interface BooksRepository extends JpaRepository<Books,Long> {
#Modifying
#Query("UPDATE Books v SET v.numComments = v.numComments + 1 WHERE v.id = :bookId")
int updateCounter(#Param("bookId")Long bookId);
}
And now the question: What next? I think I can put the update of the Books entity annotating with #PostPersist a method of the entity Comments, but I have been unsuccessful so far. I can imagine something like this:
#PostPersist //This function in the entity Comments
protected void updateBooks() {
//Likely some call to the repository here that updates the count
// in books the info we have from current entity.
}
Any idea on how to do this? Some best practices about this kind of denormalization in JPA? Better to use the database triggers?
spring not managed your entity classes and your idea is possible but you must inject BooksRepository in enttiy class then stay at you get Nullpointerexception because spring not managed enttiy classes,The reason your BooksRepository not initlaized, try also read this post Bean injection inside a JPA #Entity and anotate entity class #Configurable after
try this
#PostPersist
protected void updateBooks(Comments comment) {
int totalComment = BooksRepository.updateCounter(comment.getBookId());
System.out.println(totalComment); // see totalComment in console
}
but good aprroach in service classes after call updateCounter when insert comment
example in your CommendService : when try a insert commend after call your updateCounter
if(comment.getBookId() != null) //Simple Control
{
CommentRepository.save(comment);
BooksRepository.updateCounter(comment.getBookId());
}

Proper Way to layer Spring JPA based DAO using Spring Boot Framework

Am new to Spring Boot & JPA...
Let's say I have two entities mapped to two tables which are joined in a database.
Student-1------<-Course
Also, lets presume that the database is already created and populated.
This depicts that one student has many courses...
My Student Entity:
#Entity
public class Student {
#OneToMany(mappedBy="student")
private List<Courses> courses;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Student_Id")
private long studentId;
#Column(name = "Student_Name")
private String studentName;
protected Student() { }
// Getters & Setters
}
My Course Entity:
#Entity
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Course_Id")
private long courseId;
#Id
#Column(name = "Student_Id")
private long studentId;
#ManyToOne
#PrimaryKeyJoinColumn(name="Student_Id", referencedColumnName="Student_Id")
private Student student;
#Column(name = "Course_Name")
private String courseName;
// Getters & Setters
}
In Spring Boot's Tutorial Guides, it illustrates how to extend a CrudRepository interface, but
it doesn't specify how to setup a Spring based DAO which contains custom finder methods which use HQL and EntityManager inside it.
Is the following DAO and DaoImpl correct?
public interface CourseDao {
List<Course> findCoursesByStudentName(String studentName);
}
#Repository
public class CourseDaoImpl implements CourseDao {
#PersistenceContext
EntityManager em;
public List<Course> findCoursesByStudentName(String studentName) {
String sql = "select c.courseName" +
"from Course c, Student s " +
"where c.course_id = s.student_id " +
"and s.studentName = :studentName ";
Query query = em.createQuery(sql);
query.setParameter("studentName", studentName);
return query.getResultList();
}
}
And then in the client code, for example, in the main class:
public class Application {
#Autowired
CustomerDao dao;
public static void main (String args []) {
List<Course> courses = dao.findCoursesByStudentName("John");
}
}
Is this the standard way to use HQL inside Spring DAOs ? I've seend examples of the #Transactional annotation being prepended to the DAO class's impl (e.g. CustomerDAOImpl) ?
Please let me know if this is the write way to structure my Spring Boot app or am I supposed to extend / add to the CrudRepository only?
If someone could correct my example and point me to a URL which talks about HQL using Entities that are joined, I would be very grateful.
The Spring Boot guides didn't depict joins or DAOs - I just need to learn how to properly create finder methods which emulate select statement which return lists or data structures.
Thanks for taking the time to read this...
If I understood your question correct you do have two questions:
How to create a DAO and DAOImpl?
Where to put your Transaction annotations?
In regards to the first question I want to point out that this is a question in regards to spring-data-jpa using Hibernate as a JPA provider, not spring-boot.
Using Spring Data I usually skip completely to create a DAO but directly use a Custom Repository extending a standard one like CrudRepository. So in your case you don't even have to write more code than:
#Repository
public interface StudentRepository extends CrudRepository<Student, Long> {
List<Student> findByStudentName(String studentName);
}
Which will be sufficient and Spring Data will take care of filling it with the correct implementation if you use
#Autowired
StudentRepository studentRepo;
in your service class. This is where I also usually annotate my methods with #Transactional to make sure that everything is working as expected.
In regards to your question about HQL please look into the spring data jpa documentation, which points out that for most of the cases it should be sufficient to stick to proper named methods in the interface or go for named queries (section 3.3.3) or use the #Query annotation (section 3.3.4) to manually define the query, e.g. should work (didn't tried):
#Repository
public interface #CourseRepository extends CrudRepository<Course, Long> {
#Query("select c.courseName from Course c, Student s where c.course_id = s.student_id and s.studentName = :studentName")
public List<Course> findCoursesByStudentName(String studentName);
}
If you annotate your CourseDaoImpl with #Transactional (Assuming your have defined JpaTransactionManager correctly) You can just retrieve the Student with the matching name and call the getCourses() method to lazy load the Courses attached to that student. Since findCoursesByStudentName will run within a Transaction it will load the courses just fine.
#Repository
#Transactional(readOnly=true)
public class CourseDaoImpl implements CourseDao {
#PersistenceContext
EntityManager em;
public List<Course> findCoursesByStudentName(String studentName) {
String sql = "select s " +
"from Student s " +
"where s.studentName = :studentName ";
Query query = em.createQuery(sql);
query.setParameter("studentName", studentName);
User user = query.getSingleResult();
if(user != null) {
return user.getCourses();
}
return new ArrayList<Course>();
}
}

Hibernate and JPA always load referenced tables

I am working with Hibernate 4+ Spring MVC + Spring Data with JPA annotations:
#Entity
public class ClassOne implements Serializable{
......
#OneToMany(mappedBy = "mapper", fetch=FetchType.LAZY)
private Set<ClassTwo> element = new HashSet<ClassTwo>(0);
//more fields
//getters and setters
//equals and hashcode
}
#Entity
public class ClassTwo implements Serializable{
......
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "CEN_CEN_CODIGO", nullable = false)
private ClassOne classOne;
//more fields
//getters and setters
//equals and hashcode
}
public interface ClassOneRepository extends CrudRepository<ClassOne, Long> {
#Override
#Query("select c from ClassOne c")
public List<ClassOne> findAll();
}
#Service
public class ClassOneService {
#Autowired
private ClassOneRepository classOneRepository;
#Transactional(readOnly=true)
public List<ClassOne> findAll() {
return classOneRepository.findAll();
}
}
And finally I call thie service from my #Controller
#Autowired
ClassOneService classOneService;
I expect results ONLY from ClassOne but retrieving the JOIN values with ClassTwo and all the database tree associate. Is it possible to get only values for ONE table using this schema? Is it a cache problem or Fetching not LAZY?
EDIT: I added the relatioship between two classes
Thank you
You must have the following anotation above your Set<ClassTwo> or its getter:
#OneToMany(fetch = FetchType.LAZY, ...)
See http://docs.oracle.com/javaee/7/api/javax/persistence/OneToMany.html#fetch()
It seems to be that simple "SELECT *" JPA query returns all eagerly fetched fields for the entity.
Please refer to: JPA - Force Lazy loading for only a given query
and http://forcedotcom.github.io/java-sdk/jpa-queries.
So my solution would be to use SessionFactory to get current session and then use "classic" method
return getCurrentSession().createCriteria(persistentClass).list();
Another possible way is to create let's say a POJO object without Set which uses the same table as ClassOne.
I've just added #Lazy for each #ManyToOne and #OneToMany relationship. It seems that Spring Data needs Spring annotations but I supposed that just was necessary to add fetch = FetchType.LAZY. No more Eager behaviours ;).
Thanks for your responses

Resources