I am trying to create a generic JPA entity class that, when extended, would create a separate table for each extended entity class and also create per-entity, separate, many-to-many table for the relationships included to the generic type. I have accomplished the first step - to create per-entity tables for the general entities, but I can't figure out how I could create the corresponding many-to-many tables per class.
Here is the model and some explanation:
A #MappedSuperclass entity class for every entity in the model:
#Getter
#Setter
#MappedSuperclass
public class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "Id", nullable = false, updatable = false)
private long id;
}
An abstract class for extending the BaseEntity with the saving time and user. This is a separate class, because this is not present in some other model classes (unrelated to this issue).
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Getter
#Setter
public abstract class EntityWithSaveData extends BaseEntity {
// omitted unrelated fields...
}
A few model classes representing "linkages" that extend the abstract Linkage entity.
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Getter
#Setter
public abstract class Linkage extends EntityWithSaveData {
// omitted unrelated fields...
}
#Table
#Entity(name = "LinkageA")
#Getter
#Setter
#NoArgsConstructor
public class LinkageA extends Linkage {
#OneToMany(mappedBy = "root")
private Collection<LinkageGroup<LinkageA>> allRoot = new ArrayList<>();
#ManyToMany(mappedBy = "children")
private Collection<LinkageGroup<LinkageA>> allChildren = new ArrayList<>();
}
#Table
#Entity(name = "LinkageB")
#Getter
#Setter
#NoArgsConstructor
public class LinkageB extends Linkage {
#OneToMany(mappedBy = "root")
private Collection<LinkageGroup<LinkageB>> allRoot = new ArrayList<>();
#ManyToMany(mappedBy = "children")
private Collection<LinkageGroup<LinkageB>> allChildren = new ArrayList<>();
}
And linkage groups. There is an abstract, generic LinkageGroup class with a "root" and "children". For each "linkage" entity there is a "linkage group" entity that extends the LinkageGroup.
#Entity(name = "LinkageGroup")
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Getter
#Setter
public abstract class LinkageGroup<T extends Linkage> extends EntityWithSaveData {
#ManyToOne(targetEntity = Linkage.class)
#JoinColumn(name = "IdRootLinkage", nullable = false)
private T root;
#ManyToMany(targetEntity = Linkage.class)
private Collection<T> children = new ArrayList<>();
}
#Table
#Entity(name = "GroupA")
#Getter
#Setter
#NoArgsConstructor
public class GroupA extends LinkageGroup<LinkageA> {
// omitted unrelated fields...
}
#Table
#Entity(name = "GroupB")
#Getter
#Setter
#NoArgsConstructor
public class GroupB extends LinkageGroup<LinkageB> {
// omitted unrelated fields...
}
While Hibernate creates the "linkages" and "groups" tables just fine, the "group children" are created as a single table, that I would like to avoid and I would prefer a table per LinkageGroup entity.
Here is the generated database tables list
Is there a way to achieve what I am trying to do in this case?
Related
Say I have a column books in table authors that contains a list of book ids and some description in jsonb type.
I've managed to deserialize this and place it in an Author instance by defining the following classes:
#Entity
#Table(name = "authors")
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
public class Author {
#Id
#Column(name = "id")
private Long id;
#Type(type = "jsonb")
#Column(name = "books", columnDefinition = "jsonb")
private List<TestObject> testObjectList;
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class TestObject {
#JsonProperty("book_id")
private Long id;
#JsonProperty("book_description")
private String bookDescription;
}
However, instead of the Long id field, I'd like to fetch the whole Book entity inside TestBook class. How can I achieve this? This would be easier if the book_id field was inside the Author class, but here it is nested.
We have a base class
#SuperBuilder(toBuilder = true)
#Getter
#Setter
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
#MappedSuperclass
public class BaseEntity {
private UserEntity createUser;
private LocalDateTime createDate;
}
And an abstract class which extends this base class:
#SuperBuilder(toBuilder = true)
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity(name = "COMPANY")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class CompanyEntity extends BaseEntity {
#EqualsAndHashCode.Include
#Id
#GeneratedValue
private UUID id;
#Builder.Default
#Fetch(FetchMode.SUBSELECT)
#ManyToMany(mappedBy = "companies", fetch = FetchType.LAZY)
private Set<UserEntity> users = new HashSet<>();
}
With two classes extending the abstract class that look like this
#SuperBuilder(toBuilder = true)
#Entity(name = "SPECIAL")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class SpecialEntity extends CompanyEntity {
#NotBlank
private String customerName;
private String somethingUnimportant;
}
We have to remove the #Entity(name = "COMPANY") in order for the SuperBuilder to work. Our current #Inheritance(strategy = InheritanceType.JOINED) will therefore no longer work to combine the sub-entities into one table in the database.
How is it possible to keep the current database structure, but change to include the SuperBuilder constructor?
Is #jan-rieke around? :D
I am using jpa and have 2 entities, but in this situation :
Entity A = schemaA.tableX
Entity B = schemaB.tableX
tableX is the same but duplicated on 2 different schemas (A and B), same columns inside, and I am supposed to fill them with same data through my application.
The question is : is it possible in my code, to mappe once this tableX and somehow, data will be splited on two, one for each schema ??
I do not want :
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "TableX", schema = "A")
public class A implements Serializable {
#Id
#Column(name = "id")
private String id; <=== on table A
}
And :
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "TableX", schema = "B")
public class B implements Serializable {
#Id
#Column(name = "id")
private String id; <=== on table B
}
A dumb copy/past of : entity, repository, service, impl... because exactelly the same Table !
You could do that probably with a #MappedSuperClass:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#MappedSuperClass // <-- see this annotation
public class AbstractTable implements Serializable { // <-- name the class however you want
#Id
#Column(name = "id")
private String id;
}
And then change your two other classes to this:
#Entity
#Table(name = "TableX", schema = "A")
public class A extends AbstractTable {}
And
#Entity
#Table(name = "TableX", schema = "B")
public class B extends AbstractTable {}
Spring boot 2.2.2 ~ 2.2.4 ( the one i have tested so far)
I have an Abstract class
#MappedSuperclass
#Data
#EntityListeners(AuditingEntityListener.class)
#FilterDef(name = "tenantFilter", parameters = {#ParamDef(name = "tenantId", type = "int")})
#Filter(name = "tenantFilter", condition = "tenantId = :tenantId")
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
....
}
A personnel class extending the abstract class
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "personnel")
#AllArgsConstructor
#NoArgsConstructor
#Data
#EntityListeners(AuditingEntityListener.class)
public class Personnel extends BaseEntity {
#NotNull
#OneToOne
#JoinColumn(name = "titleID")
private Title title;
// other fields and relationships of type ManyToOne, OneToMany
}
My Jpa Repository class
public interface PersonnelRepository extends JpaRepository<Personnel, Integer> {
Optional<PersonnelData> findByFileNo(String fileNo);
Optional<PersonnelData> findByIdAndDeletedOnIsNull(Integer id);
}
In my database, I have personnel records with some of the fields being null and the relationships.
In my controller when i do findById(1), it returns null but when I do findByIdAndDeletedOnIsNull(1), it returns the record.
I have tried #NotFound(action = NotFoundAction.IGNORE) on the relationships that are null with no success.
I have tree Entities: Client, ClientConfigurationA and ClientConfigurationB.
I try to make something like:
#Entity
#Table(name = "CLIENT")
public class Client {
#Id
private int id;
#OneToOne(mappedBy = "client")
private ClientConfiguration configuration
// some getter and setter
}
#MappedSuperClass
public class ClientConfiguration {
#Id
private int id;
#OneToOne
#MapsId
protected Client client;
// getters and setters
}
#Entity(name = "CLIENT_CONF_A")
public class ClientConfigurationA extends ClientConfiguration { ... }
#Entity(name = "CLIENT_CONF_B")
public class ClientConfigurationB extends ClientConfiguration { ... }
But actually I can't define a OneToOne related to MappedSuperClass and not an Entity.
So what's the best way to implement this case ?
I already tried this solution without succes.
Thanks for reading.
It's impossible and here's why:
When hibernate processes entities it needs to know what table to join with when resolving one to one relations.
The problem arise with #MappedSuperClass is that it doesn't define table, so multiple entities could be inherited from this class, each one having different table. Hibernate just couldn't know what table to join then.
If you want to have common super class, I suggest using an abstract #Entity class.
After many attempts i finally succeeded.
#Entity
#Table(name = "CLIENT")
public class Client {
#Id
private int id;
#OneToOne(mappedBy = "client")
private ClientConfiguration configuration
// some getter and setter
}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#DiscriminatorColumn(name = "TYPE")
public abstract class ClientConfiguration {
#Id
private int id;
#Column(name = "TYPE")
private String type;
#OneToOne
#MapsId
protected Client client;
// getters and setters
}
#Entity(name = "CLIENT_CONF_A")
public class ClientConfigurationA extends ClientConfiguration { ... }
#Entity(name = "CLIENT_CONF_B")
public class ClientConfigurationB extends ClientConfiguration { ... }