Mapping abstract classes with includePaths - hibernate-search

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

Related

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.

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

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();
}

intermediate `#MappedSuperclass` failed to persist

I have three classes look like this.
#Entity
class A {
#Id #GeneratedValue #TableGenerator
private long id;
}
#MappedSuperclass
abstract class B extends A {
}
#Entity
class C extends B {
}
Should above even work? And it seems not work, at least, with EclipseLink.
I got Column 'ID' cannot be null when I tried to persist an instance of C.
I found a bug at Inheritance with abstract intermediate class using #MappedSuperclass fails to populate subclasses but I'm not sure it is exactly the same situation or not.
UPDATE per #James's answer
I'm sorry, I should written more verbosely. Yes I'm intending SINGLE_TABLE inheritance. I don't have any extended property with B nor C. It's just a hierarchical class design.
#DiscriminatorColumn
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Entity
abstract class A {
#Id #GeneratedValue #TableGenerator
private long id;
}
#DiscriminatorValue("B") #Entity // I currently should do like this.
//#MappedSuperclass // error
abstract class B<T extends SomeoneElse> extends A {
}
#DiscriminatorValue("C")
#Entity
class C extends B<James> {
}
I believe that in JPA a #MappedSuperclass must be a superclass not a subclass (as its name infers).
I'm not sure what having an #Entiy subclass as a #MappedSuperclass would mean?
What are you trying to do?
For #Entity inheritance JPA only provides three options, SINGLE_TABLE, JOINED, and TABLE_PER_CLASS. All persistence subclasses must be entities.
I assume you are using JOINED inheritance and trying to avoid a table for B. JPA does not specify a standard way of doing this. In EclipseLink you can avoid the table by making its table match the parent (#Table(name="A")).

JPA Inheritance :mapping derived entities to different tables

I use SINGLE_TABLE inheritance startegy to map my usres (see code example bellow).
Is there a way to map UnActiveRegularUser and UnActiveBusinessUser from "ACTIVE_USERS" table to another table, for example "UNACTIVE_USERS" and keep the inheritance startegy?
Note:
-The point here is to avoid code duplication between ex. RegularUser Vs UnActiveRegularUser (since they use the same properties) but still to map them to 2 different tables: "ACTIVE_USERS" and "UNACTIVE_USERS".
-strategy = InheritanceType.SINGLE_TABLE should not be changed.
-May adding another abstraction layer solve this problem?
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name = "ACTIVE_USERS")
public class User {
#Id #GeneratedValue
protected Long id;
#Column(nullable = false)
protected String name;
}
#Entity
public class RegularUser extends User{
//more getters and settres
}
#Entity
public class UnActiveRegularUser extends User{
//same getters and setters as in RegularUser
}
#Entity
public class BusinessUser extends User {
//more getters and settres
}
#Entity
public class UnActiveBusinessUser extends User {
//same getters and setters as in BusinessUser
}
Thanks,
Nathan
Persisting fields to another table won't prevent code duplication. I think you should just make UnActiveBusinessUser extend BusinessUser, and UnactiveRegularUser extend RegularUser.
Note that if a user can become unactive (i.e. it is a RegularUser and becomes an UnactiveRegularUser), inheritance is not the right solution: an object can't go from one type to another. Since it seems UnactiveRegularUser doesn't have anything more than RegularUser, I'm not sure this subclass is useful.

How do I represent this using JPA?

I would like a 'RolesPlayed' entity with the following columns
user
role
department/project/group
All the three columns above constitute a composite primary key. I would like to know if defining a column to be one of department/project/group possible ? If yes, how ? Or do I need to break the entity into DepartmentRoles, GroupRoles and ProjectRoles.
Thanks.
You could use polymorphism with an abstract base class to do that.
#Entity
public class RolePlayed {
#ManyToOne
private User user;
#ManyToOne
private Role role;
#ManyToOne
private Body body;
...
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Body {
...
}
#Entity
public class Department extends Body {
...
}
#Entity
public class Project extends Body {
...
}
#Entity
public class Group extends Body {
...
}
Check out the Polymorphism section in the Java Enterprise tutorial for a good overview.
Alternatively, you could also make the RolePlayed entity abstract, with DepartmentRole, GroupRole and ProjectRole implementations.