How to use IdClass in multiple inheritance entity composite key case ?
#IdClass(IdClassEntity1.class)
class Entity1 {
private long e1pk1;
private long e1pk2;
}
#IdClass(IdClassEntity2.class)
class Entity2 extends Entity1 {
private long e2pk1;
private long e2pk2;
}
class Entity3 extends Entity2 {
private long e3pk1;
private long e3pk2;
}
what should be IdClassEntity2 :
class IdClassEntity2 {
private long e1pk1;
private long e1pk2;
private long e2pk1;
private long e2pk2;
}
or
class IdClassEntity2 {
private IdClassEntity1 idClassEntity1;
private long e2pk1;
private long e2pk2;
}
You can use a #MappedSuperClass or #Entity to declare the #IdClass. The pk is propagated via inheritance.
For example, using #MappedSuperClass, you can do the following:
#MappedSuperClass
#IdClass(IdClassEntity1.class)
public class Entity1 {
#Id private long e1pk;
#Id private long e1pk;
...
#Entity
public class Entity2 extends Entity1 {
...
Using #Entity, follow the same paradigm
Related
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> {
}
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.
Can I use #EmbeddedId in case more than 3 relations with composite key ?
relation
parent -> child -> grandchild -> great-grandchild
parent
#Entity
#Table(name="mig_club_info")
public class MigGolfClub {
#Id #Column(name = "club_id")
private String club_id;
}
child
#Embeddable
public class MigCourseId implements Serializable{
private String club_id;
private String course_id;
}
#Entity
#Table(name="mig_course_info")
public class MigCourse {
#EmbeddedId
private MigCourseId migCourseId;
#MapsId("club_id")
#ManyToOne
#JoinColumn(name = "club_id")
private MigGolfClub migGolfClub;
}
grandchild
#Embeddable
public class MigHoleId implements Serializable{
private MigCourseId migCourseId;
private int hole_num;
}
#Entity
#Table(name="mig_hole_info")
public class MigHole {
#EmbeddedId
private MigHoleId migHoleId;
#MapsId("migCourseId")
#ManyToOne
#JoinColumns( {
#JoinColumn(name="club_id"),
#JoinColumn(name="course_id")
})
private MigCourse migCourse;
}
great-grandchild
#Embeddable
public class MigTeeId implements Serializable{
private MigHoleId migHoleId;
private String t_name;
}
#Entity
#Table(name="mig_tee_info")
public class MigTee {
#EmbeddedId
private MigTeeId migTeeId;
#MapsId("migHoleId")
#ManyToOne
#JoinColumns( {
#JoinColumn(name="club_id"),
#JoinColumn(name="course_id"),
#JoinColumn(name="hole_num")
})
private MigHole migHole;
}
I tested and got a error message
nested exception is org.hibernate.AssertionFailure: Unexpected nested component on the referenced entity when mapping a #MapsId: xxx.xxx.xxx.MigHole"
I succeeded with grandchild (2-relations) , but I failed with greate-grandchild (3-relations)
Can't I use #EmbeddedId in case more than 3 relations with composite key?? --;;
I have the following tables in my DB:
statement:
id | created_date | message
and
statement_configuration
id | name | currency
and
statement_balances
statement_id | statement_configuration_id | balance
Where the statement_balances table has a composite primary key on statement_id and statement_configuration_id.
My Statement entity looks like this:
public class Statement implements Serializable {
#Id
private long id;
#Column
private String message
//I'm not sure of which annotations I need here
#OneToMany
private Map<Long, StatementBalance> statementBalancesByConfigId;
....
}
The StatementBalances entity looks like this:
public class Statement implements Serializable {
#Id
private long statmentId;
#Id
private long statementConfigurationId;
#Column
private long balance;
....
}
My goal is to build a Map of type Map<Long, StatementBalances> inside my Statement entity. The map will map the statement_configuration_id to a balance; allowing me to get all the StatementBalances that are linked to this Statement (keyed by statement_configuration_id).
Is it possible to build this map using JPA annotations?
Yes this is possible. An example solution:
#Entity
public class Statement implements Serializable {
#Id
private long id;
private String message;
#OneToMany(mappedBy = "statementId")
#MapKey(name = "statementConfigurationId")
private Map<Long, StatementBalances> statementBalancesByConfigId;
}
#Entity
#Table(name = "statement_configuration")
public class StatementConfiguration implements Serializable {
#Id
private long id;
#OneToMany(mappedBy = "statementConfigurationId")
private Collection<StatementBalances> statementBalances;
private String name;
private String currency;
}
The StatementBalancesId composite primary key class and StatementBalances entity class allow modeling a ternary association by creating of two bidirectional relationships between them:
public class StatementBalancesId implements Serializable {
long statementId;
long statementConfigurationId;
// requires no-arg constructor, equals, hashCode
}
#Entity
#Table(name = "statement_balances")
#IdClass(StatementBalancesId.class)
public class StatementBalances implements Serializable {
#Id
#ManyToOne
#JoinColumn(name="statement_configuration_id")
private StatementConfiguration statementConfigurationId;
#Id
#ManyToOne
#JoinColumn(name="statement_id")
private Statement statementId;
#Column
private long balance;
}
The database tables created this way are identical as those in the question.
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.