I have read through many questions and tried to solve the following problem with Java generics and #MappedSuperclass. Important for me is that I have to have different tables as you would get with TABLE_PER_CLASS.
This is the goalI want to achieve in Spring-Boot:
I am stuck because examples like these are not representing my more complex goal from the image above.
The different inheriting classes (ZooAnimal, CircusAnimal) have attributes that must not be null AND must not be used in the respective opposite class.
E.g. an object of ZooAnimal may have
#NotNull
int numberOfZoosSeen;
and an object of CircusAnimal may have
#NotNull
boolean hasEatenClown;
That's why they need their own database tables.
Is it technically possible and if no, is there a better design that ensures that the entities Zoo, Circus, ZooAnimal and CircusAnimal have their own tables?
Since Animal and Institution are abstract, I would question the need to declare any association mappings in them directly.
You could simply use the following approach:
#MappedSuperclass
public abstract class Institution {
#Id
#GeneratedValue
private int id;
public abstract List<? extends Animal> getAnimals();
...
}
#Entity
public class Zoo extends Institution {
#OneToMany(mappedBy = "institution")
private List<ZooAnimal> animals;
public List<ZooAnimal> getAnimals() { //covariant type
return animals;
}
}
#MappedSuperclass
public abstract class Animal {
#Id
#GeneratedValue
private int id;
public abstract Institution getInstitution();
...
}
#Entity
public class ZooAnimal extends Animal {
#ManyToOne
private Zoo institution;
public Zoo getInstitution() { //covariant type
return institution;
}
}
Yes it's possible to implement these classes using TABLE_PER_CLASS strategy with generics, but you have to use #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) annotation instead of #MappedSuperClass.
To know more about TABLE_PER_CLASS strategy visit jpa advanced mappings
To prepare your abstract classes using generics all you have to do is understand the relation between classes before using generics. After that you have to understand recursive generics concept to implement your case using generics.
For more information's visit introduction to generics
Here is the implementation of classes:-
Institution class
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Institution<T extends Institution<T, S>, S extends Animal<S, T>> {
#Id
#GeneratedValue
private int id;
//add properties, constructors and methods
#OneToMany(targetEntity = Animal.class, mappedBy = "institution", cascade = CascadeType.ALL)
private List<S> animals;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<S> getAnimals() {
return animals;
}
public void setAnimals(List<S> animals) {
this.animals = animals;
}
}
Animal class
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Animal<T extends Animal<T, S>, S extends Institution<S, T>> {
#Id
#GeneratedValue
private int id;
//add properties, constructors and methods
#ManyToOne(targetEntity = Institution.class, cascade = CascadeType.ALL)
private S institution;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public S getInstitution() {
return institution;
}
public void setInstitution(S institution) {
this.institution = institution;
}
}
Important Note:- During implementing the entities using generics you must specify the targetEntity class in #OneToOne, #OneToMany, #ManyToOne and #ManyToMany annotations as shown in the above code because JPA cannot detect the target entity in generics.
Zoo and ZooAnimal classes
#Entity
public class Zoo extends Institution<Zoo, ZooAnimal> {
//add properties, constructors and methods
}
#Entity
public class ZooAnimal extends Animal<ZooAnimal, Zoo> {
//add properties, constructors and methods
}
Circus And CircusAnimal
#Entity
public class Circus extends Institution<Circus, CircusAnimal> {
//add properties, constructors and methods
}
#Entity
public class CircusAnimal extends Animal<CircusAnimal, Circus> {
//add properties, constructors and methods
}
As you can see in the implementation of subclasses, there is no need to override getAnimals() and getInstitution() methods to change their return types because we already specified its return types using generics.
Overview
I've got an #Entity with an #EmbeddedId composite key. The entities are exposed over a REST api which uses a BackendIdConverter to convert the id string back to an instance of the #Embeddable composite key. My understanding is that this is then used to identify an instance of the #Entity, but that isn't happening.
Question
How is the #Embeddable composite key resolved back to an #Entity?
Code
#Entity
public class MyEntity {
#EmbeddedId
private MyEntityIdentifier id;
#Embeddable
public class MyEntityIdentifier implements Serializable {
public static final String COMPOSITE_KEY_DELIMITER = "_#_";
#Column
private String idPartOne;
#Column
#Temporal(TemporalType.DATE)
private Date idPartTwo;
#Component
public class StringToMyEntityIdentifierConverter implements BackendIdConverter {
#Override
public Serializable fromRequestId(String id, Class<?> aClass) {
String[] split = id.split(COMPOSITE_KEY_DELIMITER);
String idPartOne = split[0];
Date idPartTwo = Date.valueOf(split[1]);
return new MyEntityIdentifier(fullName, lastUpdated);
}
public interface MyEntityRepository extends JpaRepository<MyEntity, MyEntityIdentifier> {
}
Given this class:
#MappedSuperclass
public abstract class AbstractEntity {
int id;
public void setId(int id) { this.id = id; }
public int getId() { return id; }
// other mappings
}
I want to define an entity:
#Entity
public class SomeEntity extends AbstractEntity {
#Override
#Id // or #OneToOne etc.
public int getId() { return id; }
}
But this fails with a "No identifier specified"
(or a "Could not determine type for") error on SomeEntity. If I remove the getter from the superclass it works. Can't I do this override strategy? Why not, or if yes - how?
Adding
#AttributeOverride(name = "id", column = #Column(name = "ID"))
to the subclass does not change the error.
For you to create an entity class there are requirements that the class must meet. Ex. must have a public/private constructor.
Here is the list of requirements:
http://docs.oracle.com/javaee/5/tutorial/doc/bnbqa.html
Hope this helps.
I have superclass:
#MappedSuperclass
public abstract class BaseEntity {
#Id #GeneratedValue
private Long id;
#Version
private long version;
}
and two subclasses:
#Entity
#Table(name = "\"user\"")
public class User extends BaseEntity {
private String username;
#org.hibernate.annotations.Type(type = "yes_no")
private boolean isAdmin;
// constructor/getters/setters etc.
}
#Entity
public class Product extends BaseEntity {
public String name;
public BigDecimal price;
// constructor/getters/setters etc.
}
I can query for all subclasses using code:
entityManager.unwrap(Session.class)
.createCriteria(BaseEntity.class)
.list()
.forEach(x -> System.out.println(x));
how I can get the same results via JPA (without unwrap, is it possible?). I tried using createQuery("from BaseEntity") but get BaseEntity not mapped exception.
EDIT: I know that this will result in two SELECT statement. And it must be MappedSuperclass - I would like to not change that.
I have a problem with abstract class where we implement an interface.
Now the interface is in our implementation of the abstract class other than the other implementation.
I'll show you the code here:
#MappedSuperclass
public abstract class AbstractOrder {
#Id
#GeneratedValue
private Long idOrder;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="datum")
private Date date = new Date();
#OneToMany(targetEntity=IOrderLine.class)
private List<IOrderLine> orderLines = new ArrayList<IOrderLine>();
private String refOrder;
...
}
#Entity
#Table(name="supplyorders")
public class SupplyOrder extends AbstractOrder implements Comparable<SupplyOrder>, Serializable {
#ManyToOne
private Supplier supplier;
#Enumerated(EnumType.STRING)
private SupplyOrderStatus status = SupplyOrderStatus.TOBESUPPLIED;
#ElementCollection
private Set<CustomerOrder> customerOrders = new HashSet<CustomerOrder>();
...
}
#Entity
#Table(name="customerorders")
public class CustomerOrder extends AbstractOrder implements commparable<CustomerOrder>,Serializable {
#ManyToOne
private Customer customer;
#ManyToOne
private Place place;
#ManyToOne
private User vendor;
private double deposit;
#Enumerated(EnumType.STRING)
private OrderStatus status = OrderStatus.CREATED;
}
#MappedSuperclass
public interface IOrderLine {
double getSubTotal();
int getQuantity();
Furniture getFurniture();
}
#Entity
#Table(name="supplyorderlines")
public class SupplyOrderLine implements IOrderLine, Serializable {
#Id
#GeneratedValue
private Long id;
#OneToMany
private List<CustomerOrderLine> customerOrderLines = new ArrayList<CustomerOrderLine>();
...
}
and of course a class CustormerOrderLine that implements IOrderLine.
Now for the supplyOrder they have supplyOrderLines in them and the customerOrder has the CustomerOrderLine in them.
The fault we get is that the abstract class doesn't know what implementation to take of the interface IOrderLine.
How can I override the field of orderLines from the abstract class in the implementation class and point to the implementation of the IOrderLine with annotations?
Thx in advance.
Chillworld
In Java you cannot instantiate an interface.
You can only instantiate actual classes that implement interfaces.
There's actually no such thing as IOrderLine.class.
You probably want to declare your orderLines field in your sub-classes.
In the sub-classes you can declare the field with a concrete class (that will map to a real database table).
If you need/want to use the abstract class to refer to your line items generically (this seems like a really good idea), you could use an abstract method that returns an interface.
Here's an example:
#MappedSuperclass
public abstract class AbstractOrder {
#Id
#GeneratedValue
private Long idOrder;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="datum")
private Date date = new Date();
// This abstract method will be implemented by sub-classes
public abstract List<IOrderLine> getOrderLines();
}
Then you can add the fields in your sub-classes and implement the abstract method that returns them:
#Entity
#Table(name = "supplyorderlines")
public class SupplyOrderLine implements IOrderLine, Serializable {
#Id
#GeneratedValue
private Long id;
#OneToMany(targetEntity = SupplyOrderLine.class)
private List<SupplyOrderLine> customerOrderLines;
#Override
public List<IOrderLine> getOrderLines() {
return customerOrderLines;
}
}
If this wasn't about JPA entities, you could also do something like this:
public abstract class PojoClass {
private Long idOrder;
private Date date = new Date();
private List<? extends IOrderLine> orderLines = new ArrayList<? extends IOrderLine>();
}
However, I don't think this is an option in a JPA entity.
That's because your entity class needs to map to a concrete class and database table.