java.lang.IllegalArgumentException: You have provided an instance of an incorrect PK class for this find operation. - jpa

I'm trying to create a URI and returns an object looking for NIF. (One custom filter)
I have tried to replicate the search by id but does not work, the truth, I'm not sure what I do. I have two classes with these functions
ClientesAbstractFacade.java
public T findNif(Object nif) {
return getEntityManager().find(entityClass, nif);
}
lientesFacadeREST.java
#GET
#Path("nif/{nif}")
#Produces({MediaType.APPLICATION_JSON})
public Clientes findNif(#PathParam("nif") String nif) {
return super.findNif(nif);
}
And here POJO
#Entity
#Table(name = "clientes")
public class Clientes implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
As you can see, I'm trying to do custom searches, something easy, and then implement a login.
But I can not even filter by nif, these return error 500
java.lang.IllegalArgumentException: You have provided an instance of an incorrect PK class for this find operation. Class expected : class java.lang.Integer, Class received : class java.lang.String.

The exception says it all:
IllegalArgumentException: You have provided an instance of an incorrect PK class for this find operation. Class expected : class java.lang.Integer, Class received : class java.lang.String
The getEntityManager().find(entityClass, nif) is working on the Primary Key column of your Table. This is, as the exception states, an Integer.
I guess you want to use your NamedQueries and have thus to use createNamedQuery-methods of the EntityManager.
So your find-method should look something like that:
public T findNif(String nif) {
return (T) getEntityManager().createNamedQuery("Clientes.findByNif", Clientes.class)
.setParameter("nif", nif).getSingleResult();
}

Related

How can I use JPA Query Methods to return an OR condition with NULL?

Am trying to create a Query that either matches all rows that equal tier or are NULL. Using Query Methods as described in Spring JPA Docs. The Default implementation below works if I just pass in the tier:-
#Entity
#Table(name = "tier")
class UuTier {
Long id;
Long tierId;
}
#Entity
#Table(name = "user")
class User {
#OneToOne
#JoinColumn(name="tier_id")
UuTier uuTier;
// Other Relationships
}
public interface UserRepository extends Repository<User, Long> {
List<User> findByTier_Id(#Param("tier")Long tier);
}
What I need is something like this, which is throwing an error " No property null found for type User". Can I achieve this ask using Query Methods?:-
public interface UserRepository extends Repository<User, Long> {
List<User> findByTierOrNull_Id(#Param("tier")String tier);
}
Following up from one of the responders (who for some reason deleted her post) - I got this to work!!
#Query("SELECT entity FROM User entity LEFT JOIN UuTier uuTier ON entity.uuTier.tier = uuTier.tier"
+ " WHERE entity.uuTier.tier = :tier OR entity.uuTier.tier IS NULL")
public List<User> findByTierOrNull_Id(#Param("tier") Long tier);

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

findAll clashes with findAll with CrudRepository in Projections

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

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.

Mapping abstract classes with includePaths

I have a question regarding mapping of Hibernate Search and using an abstract base class.
I'm getting the following error
Caused by: org.hibernate.search.SearchException: Found invalid #IndexedEmbedded->paths configured on class nl.project.model.social.AbstractGroup, member language: language.id
at org.hibernate.search.engine.spi.AbstractDocumentBuilder.validateAllPathsEncountered(AbstractDocumentBuilder.java:901)
at org.hibernate.search.engine.spi.AbstractDocumentBuilder.checkForIndexedEmbedded(AbstractDocumentBuilder.java:880)
at org.hibernate.search.engine.spi.AbstractDocumentBuilder.initializeMemberLevelAnnotations(AbstractDocumentBuilder.java:489)
at org.hibernate.search.engine.spi.AbstractDocumentBuilder.initializeClass(AbstractDocumentBuilder.java:391)
at org.hibernate.search.engine.spi.AbstractDocumentBuilder.<init>(AbstractDocumentBuilder.java:174)
at org.hibernate.search.engine.spi.DocumentBuilderContainedEntity.<init>(DocumentBuilderContainedEntity.java:60)
at org.hibernate.search.spi.SearchFactoryBuilder.initDocumentBuilders(SearchFactoryBuilder.java:396)
at org.hibernate.search.spi.SearchFactoryBuilder.buildNewSearchFactory(SearchFactoryBuilder.java:222)
at org.hibernate.search.spi.SearchFactoryBuilder.buildSearchFactory(SearchFactoryBuilder.java:146)
at org.hibernate.search.event.impl.FullTextIndexEventListener.initialize(FullTextIndexEventListener.java:130)
at org.hibernate.search.hcore.impl.HibernateSearchIntegrator.integrate(HibernateSearchIntegrator.java:83)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:301)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1750)
Based on the following mapping configuration
#Entity
#Table
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "DTYPE", discriminatorType= DiscriminatorType.STRING, length = 3)
#Indexed
public abstract class AbstractGroup implements Serializable, IEntity, IPhoto{
protected Language language;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumn(name="FK_LanguageId")
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE,region=CacheRegion.NEVERCHANGE)
#NotNull
#IndexedEmbedded(includePaths={"id"})
public Language getLanguage() {
return language;
}
}
#Entity
#DiscriminatorValue(value = "GRP")
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE,region=CacheRegion.GROUP)
public class Group extends AbstractGroup{
#Entity
#DiscriminatorValue(value = "PGE")
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE,region=CacheRegion.GROUP)
public class Page extends AbstractGroup{
I've tried putting #Indexed on the subclass but this gives the same error.
The #Indexed annotation should indeed be on the sub classes. However, I am not sure what this should have to to with te discriminator column. JPA and Search annotations should be orthogonal. Two different things really. Btw, how does your Language entity look like? See also https://forum.hibernate.org/viewtopic.php?f=9&t=993097&hilit=abstract+base+class