findAll clashes with findAll with CrudRepository in Projections - jpa

Login
#ApiModel
#Entity
public class Login {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private LocalDateTime loginDateTime;
/** Other fields ***/
}
LoginDateOnly
interface LoginDateOnly {
#Value("#{target.loginDateTime.toLocalDate()}")
LocalDate getDateFromLoginDateTime();
}
LoginRepository
#RepositoryRestResource(collectionResourceRel = "login", path = "login")
public interface LoginRepository extends PagingAndSortingRepository<Login, Long> {
Collection<LoginDateOnly> findAll();
/** Other query methods **/
}
I simply want to get all my Login record, with LocalDate part of my loginDateTime selected/projected using a http://host/api/login. But currently I'm encountering a clash with CrudRepository's findAll(). How to solve this as much as possible using projection. I'm making #Query and #NamedQuery my last resort.

A findAll method signature is:
List<T> findAll();
If you want to override it you cannot use another signature.
All you need to get a list of your projections is define another method for this, for example:
Collection<LoginDateOnly> findAllBy();
But as I can see you are using the Spring Data REST, so in this case you don't need to define a new method. You should firstly add annotation #Projection to your projection:
#Projection(name = "loginDateOnly", types = Login.class)
interface LoginDateOnly {
//...
}
Then use its name in the request url:
GET http://host/api/login?projection=loginDateOnly
See more info in the doc: Projections and Excerpts

Related

How can I get data from #DBRef document using #Query -> Spring data mongo

I need help to get the data from another document I have the following class.
#Data
#Document(collection = "tmVersion")
public class TmVersion {
#Id
private String id;
private String cVrVersionId;
#DBRef
private TaApplicationVersion taApplicationVersion;
}
and
#Data
#Document(collection = "taApplicationVersion")
public class TaApplicationVersion {
#Id
private String id;
private String dVrAppName;
private String dVrAppCode;
}
This is my repository in which I map what I want to be shown but in taApplicationVersion I need to show all this object also how is it done?
#Query(value="{}", fields="{'cVrVersionId': 1, 'taApplicationVersion.dVrAppName': 2,
'dVrVersionNumber': 3}")
Page<TmVersion> getAllVersionWithOutFile(Pageable pageable)
Couple of things to mention here.
If you want this kind of join between tables, then you need to rethink your choice of Mongodb as database. No Sql Databases thrive on the fact that there is very less coupling between tables(collections). So if you are using #DBRef, it negates that. Mongodb themselves do not recommend using #DBRef.
This cannot be achieved with the method like you have in the repository. You need to use Projections. Here is the documentation for that.
Create a Porjection interface like this. Here you can control which fields you need to include in the Main class(TmVersion)
#ProjectedPayload
public interface TmVersionProjection {
#Value("#{#taApplicationVersionRepository.findById(target.taApplicationVersion.id)}")
public TaApplicationVersion getTaApplicationVersion();
public String getId();
public String getcVrVersionId();
}
Change the TmVersionRepository like this
public interface TmVersionRepository extends MongoRepository<TmVersion, String> {
#Query(value="{}")
Page<TmVersionProjection> getAllVersionWithOutFile(Pageable pageable);
}
Create a new Repository for TaApplicationVersion. You can add #Query on top of this method and control which fields from subclass needs to be returned.
public interface TaApplicationVersionRepository extends MongoRepository<TaApplicationVersion, String> {
TaApplicationVersion findById(String id);
}

Kotlin inheritance and JPA

I'm trying to implement inheritance with Kotlin and JPA. My abstract base class (annotated with #Entity) holds the ID (annotated with #Id and #GeneratedValue) and other metadata, like createDate, etc. I'm getting several errors from Hibernate, one for each field except the ID:
org.hibernate.tuple.entity.PojoEntityTuplizer - HHH000112: Getters of lazy classes cannot be final: com.example.BaseEntity.createDate
As I've read I need to include the open keyword for each property.
I have 3 questions regarding this:
Why do I have to do that in the superclass, and don't need in subclass? I'm not overriding those properties.
Why isn't it complaining about the ID?
It seems to work without the open keyword, then why is the error logged?
Edit:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
abstract class BaseEntity(
#Id #GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long = 0,
val createdAt: Instant = Instant.now()
)
#Entity
class SubClass(
val someProperty: String = ""
) : BaseEntity()
I'm using the JPA plugin for Gradle, which I believe creates the noarg constructor, that's why I don't have to specify everything nullable.
Thank you!
The logged error has to do with lazy loading.
Hibernate extends entities at runtime to enable it. It is done by intercepting an access to properties when an entity is loaded lazily.
Kotlin has flipped the rules and all classes are final by default there. It is the reason why we're advised to add an open keyword.
If a property is not open hibernate cannot intercept access to it because final methods cannot be overridden. Hence the error.
Why isn't it complaining about the ID?
Because #Id is always loaded. There is no need to intercept access to it.
It seems to work without the open keyword, then why is the error logged?
The key word here is seems. It may introduce subtle bugs.
Consider the following #Entity:
#Entity
public class Book {
#Id
private Long id;
private String title;
public final Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public final String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
And the #Test:
#Test
public void test() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// signal here
Book book = new Book();
book.setId(1L);
book.setTitle("myTitle");
entityManager.persist(book);
// noise
entityManager.getTransaction().commit();
entityManager.close();
entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// signal
Book reference = entityManager.getReference(Book.class, 1L);
String title = reference.getTitle();
assertNull(title); // passes
entityManager.getTransaction().commit();
entityManager.close();
}
This test passes but it should not (and fails if getTitle is not final).
This would be hard to notice
Why do I have to do that in the superclass, and don't need in subclass? I'm not overriding those properties.
Looks like Hibernate gives up when it sees final #Entity.
Add open to SubClass and you will the precious:
2019-05-02 23:27:27.500 ERROR 5609 --- [ main] o.h.tuple.entity.PojoEntityTuplizer : HHH000112: Getters of lazy classes cannot be final: com.caco3.hibernateanswer.SubClass.someProperty
See also:
final methods on entity silently breaks lazy proxy loading
How to avoid initializing HibernateProxy when invoking toString() on it? - my old question (note that Hibernate uses Byte Buddy these days).
PS
Did you forget to include #MappedSuperclass on BaseEntity?
Without the annotation it should fail with something like:
org.hibernate.AnnotationException: No identifier specified for entity: com.caco3.hibernateanswer.SubClass

How can I get a list of a single field value from an entity?

I am working on a Jhipster app Java service and Angular 5 UI. I have an entity working fine, but I need to get a list of one of the fields (customer) from that entity to display in the UI.
In this case it's a single table I am using which contains the client name, so I am trying to get a distinct list returned for read only.
I have tried creating a custom repository and added a function into the service, Impl class and resource class.
Upon startup its failing with cannot find a property getClientNameList on the entity.
I have show a snippet of the code from the Entity class, the custom repository and the method I added into the PostsServiceImpl class.
Can someone please steer me in the right direction?
Thanks.
// Entity Class //
#Entity
#Table(name = "posts")
public class Posts implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="client_name")
private String clientName;
// Other fields here
...
}
// Custom Repository //
#Repository
public interface JobsRepositoryCustom {
List<String> getClientNameList();
}
// PostsServiceImpl //
public class PostsServiceImpl implements PostsService {
EntityManager entityManager;
public List<String> getClientNameList() {
Query query = entityManager.createNativeQuery("SELECT clientName FROM Posts", Posts.class);
return query.getResultList();
}
}
Your error might be more specifically that clientName is not found. It is not found because if you run a native query you need to use the database column names.
So change:
"SELECT clientName FROM Posts"
to
"SELECT client_name FROM Posts"

how to filter out entity object inside entity in rest api

I am using Spring Boot to implement rest api. There are three entities SeqTb, PairTb, and GroupTb and they are nested. SeqTb has manytoone with PairTb. PairTb has onetomany relationship with SeqTb and also manytoone with GroupTb.
//SeqTb.java
#Entity
#Table(name="SEQ_TB")
public class SeqTb implements Serializable {
.......
#ManyToOne
#JoinColumn(name="PAIR_ID")
private PairTb pairTb;
......
}
// PairTb.java
#Entity
#Table(name="PAIR_TB")
#NamedQuery(name="PairTb.findAll", query="SELECT p FROM PairTb p")
public class PairTb implements Serializable {
#ManyToOne
#JoinColumn(name="GROUP_ID")
private GroupTb groupTb;
#OneToMany(mappedBy="pairTb", cascade=CascadeType.ALL)
private List<SeqTb> seqTbs;
}
//GroupId.java
#Entity
#Table(name="GROUP_TB")
public class GroupTb implements Serializable {
//bi-directional many-to-one association to PairTb
#OneToMany(mappedBy="groupTb", cascade=CascadeType.ALL)
private List<PairTb> pairTbs;
}
In my controller GET request with analysisId was handled in the following way:
#RequestMapping(
value = "/api/seqs/{analysis_id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SeqTb> getSeqByAnalysisId(#PathVariable("analysis_id") String analysis_id) {
SeqTb seq = seqService.findByAnalysisId(analysis_id);
return new ResponseEntity(seq, HttpStatus.OK);
}
I also create a bean class SeqServiceBean that extends the interface SeqService which in turn calls methods from the following JPA repository for query.
//SeqRepository.java
#Repository
public interface SeqRepository extends JpaRepository<SeqTb, Integer> {
#Override
public List<SeqTb> findAll();
public List<SeqTb> findByAnalysisId(String analysisId);
}
When I query a SeqTb object with SeqTb.PairTb == null, the api works just fine. However, if the analysisId I put in the url belongs to a SeqTb record that associates with a pairId which in turn belongs to a groupId, the program would go nuts. Below is the output, the first part output is correct (bold text). After that it keeps printing PairTb and GroupTb in loops (repeating keywords pairTb, groupTb).
{"rowId":8,"analysisId":"cce8d2c2-a6dc-4ee9-ba97-768f058abb50","analyteCode":"D","center":"UCSC",
"pairTb":{"rowId":4,"pairCode":"01ad975d-c2ed-4e4d-bd3b-c9512fc9073c","groupTb":{"rowId":1,"groupName":"PAWG_pilot-50","pairTbs":[{"rowId":1,"pairCode":"00ad0ffe-2105-4829-a495-1c2aceb5bb31","groupTb":{"rowId":1,"groupName":"PAWG_pilot-50","pairTbs":
Meanwhile I got lots of errors from tomcat server:
Caused by: java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:565) ~[tomcat-embed-core-8.0.32.jar:8.0.32]
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:212) ~[tomcat-embed-core-8.0.32.jar:8.0.32]
How do I ignore the nested entity object inside an entity and get only the meaning columns?
You can also annotate a property with #JsonIgnore in order to not output that field.
Found the solution. Created a value object that only contains the specific columns from entity and leave out the nested entity object. And it works.

Spring Data JPA And NamedEntityGraphs

currently I am wrestling with being able to fetch only the data I need. The findAll() method needs to fetch data dependant on where its getting called.
I do not want to end up writing different methods for each entity graph.
Also, I would avoid calling entitymanagers and forming the (repetitive) queries myself.
Basicly I want to use the build in findAll method, but with the entity graph of my liking. Any chance?
#Entity
#Table(name="complaints")
#NamedEntityGraphs({
#NamedEntityGraph(name="allJoinsButMessages", attributeNodes = {
#NamedAttributeNode("customer"),
#NamedAttributeNode("handling_employee"),
#NamedAttributeNode("genre")
}),
#NamedEntityGraph(name="allJoins", attributeNodes = {
#NamedAttributeNode("customer"),
#NamedAttributeNode("handling_employee"),
#NamedAttributeNode("genre"),
#NamedAttributeNode("complaintMessages")
}),
#NamedEntityGraph(name="noJoins", attributeNodes = {
})
})
public class Complaint implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private long id;
private Timestamp date;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "customer")
private User customer;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "handling_employee")
private User handling_employee;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="genre")
private Genre genre;
private boolean closed;
#OneToMany(mappedBy = "complaint", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<ComplaintMessage> complaintMessages = new ArrayList<ComplaintMessage>();
//getters and setters
}
And my JPARepository
#Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
List<Complaint> findByClosed(boolean closed);
#EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH)
#Override
List<Complaint> findAll(Sort sort);
}
We ran into a similar problem and devised several prospective solutions but there doesn't seem to be an elegant solution for what seems to be a common problem.
1) Prefixes. Data jpa affords several prefixes (find, get, ...) for a method name. One possibility is to use different prefixes with different named graphs. This is the least work but hides the meaning of the method from the developer and has a great deal of potential to cause some non-obvious problems with the wrong entities loading.
#Repository
#Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
#EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
User findByUserID(int id);
#EntityGraph(value = "User.membershipYears", type = EntityGraphType.LOAD)
User readByUserId(int id);
}
2) CustomRepository. Another possible solutions is to create custom query methods and inject the EntityManager. This solution gives you the cleanest interface to your repository because you can name your methods something meaningful, but it is a significant amount of complexity to add to your code to provide the solution AND you are manually grabbing the entity manager instead of using Spring magic.
interface UserRepositoryCustom {
public User findUserWithMembershipYearsById(int id);
}
class UserRepositoryImpl implements UserRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public User findUserWithMembershipYearsById(int id) {
User result = null;
List<User> users = em.createQuery("SELECT u FROM users AS u WHERE u.id = :id", User.class)
.setParameter("id", id)
.setHint("javax.persistence.fetchgraph", em.getEntityGraph("User.membershipYears"))
.getResultList();
if(users.size() >= 0) {
result = users.get(0);
}
return result;
}
}
#Repository
#Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
#EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
User findByUserID(int id);
}
3) JPQL. Essentially this is just giving up on named entity graphs and using JPQL to handle your joins for you. Non-ideal in my opinion.
#Repository
#Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
#EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
User findByUserID(int id);
#Query("SELECT u FROM users WHERE u.id=:id JOIN??????????????????????????")
User findUserWithTags(#Param("id") final int id);
}
We went with option 1 because it is the simplest in implementation but this does mean when we use our repositories we have have to look at the fetch methods to make sure we are using the one with the correct entity graph. Good luck.
Sources:
JPA EntityGraph with different views using Spring
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods
I don't have enough reputation to post all of my sources. Sorry :(
We had the same issue and built a Spring Data JPA extension to solve it :
https://github.com/Cosium/spring-data-jpa-entity-graph
This extension allows to pass named or dynamically built EntityGraph as an argument of any repository method.
With this extension, you would have this method immediatly available:
List<Complaint> findAll(Sort sort, EntityGraph entityGraph);
And be able to call it with an EntityGraph selected at runtime.
Use #EntityGraph together with #Query
#Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
#EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH)
#Query("SELECT c FROM Complaint ORDER BY ..")
#Override
List<Complaint> findAllJoinsButMessages();
#EntityGraph(value = "allJoins" , type=EntityGraphType.FETCH)
#Query("SELECT c FROM Complaint ORDER BY ..")
#Override
List<Complaint> findAllJoin();
...
}
Using the #EntityGraph annotation on a derived query is possible, as I found out from This article. The article has the example:
#Repository
public interface ArticleRepository extends JpaRepository<Article,Long> {
#EntityGraph(attributePaths = "topics")
Article findOneWithTopicsById(Long id);
}
But I don't think there's anything special about "with" and you can actually have anything between find and By. I tried these and they work (this code is Kotlin, but the idea is the same):
interface UserRepository : PagingAndSortingRepository<UserModel, Long> {
#EntityGraph(attributePaths = arrayOf("address"))
fun findAnythingGoesHereById(id: Long): Optional<UserModel>
#EntityGraph(attributePaths = arrayOf("address"))
fun findAllAnythingGoesHereBy(pageable: Pageable): Page<UserModel>
}
The article had mentioned the caveat that you can't create a method similar to findAll which will query all records without having a By condition and uses findAllWithTopicsByIdNotNull() as an example. I found that just including By by itself at the end of the name was sufficient: findAllWithTopicsBy(). A little more terse but maybe a little more confusing to read. Using method names which end with just By without any condition may be in danger of breaking in future versions in Spring since it doesn't seem like an intended use of derived queries name.
It looks like the code for parsing derived query names in Spring is here on github. You can look there in case you're curious about what's possible for derived queries repository method names.
These are the spring docs for derived queries.
This was tested with spring-data-commons-2.2.3.RELEASE
EDIT: this doesn't actually work. Ended up having to go with https://github.com/Cosium/spring-data-jpa-entity-graph. The default method LOOKS correct, but doesn't successfully override the annotations.
Using JPA, what I found works is to use a default method, with a different EntityGraph annotation:
#Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
List<Complaint> findByClosed(boolean closed);
#EntityGraph(attributePaths = {"customer", "genre", "handling_employee" }, type=EntityGraphType.FETCH)
#Override
List<Complaint> findAll(Sort sort);
#EntityGraph(attributePaths = {"customer", "genre", "handling_employee", "messages" }, type=EntityGraphType.FETCH)
default List<Complaint> queryAll(Sort sort){
return findAll(sort);
}
}
You don't have to do any of the re-implementation, and can customize the entity graph using the existing interface.
Can you try create EntiyGraph name with child that you will request and give same name to the find all method.
Ex:
#EntityGraph(value = "fetch.Profile.Address.record", type = EntityGraphType.LOAD)
Employee getProfileAddressRecordById(long id);
For your case:
#NamedEntityGraph(name="all.Customer.handling_employee.genre", attributeNodes = {
#NamedAttributeNode("customer"),
#NamedAttributeNode("handling_employee"),
#NamedAttributeNode("genre")
})
method name in repository
#EntityGraph(value = "all.Customer.handling_employee.genre" , type=EntityGraphType.FETCH)
findAllCustomerHandlingEmployeeGenre
This way you can keep track of different findAll methods.