I have one JPA mapped superclass (joined inheritance strategy) that haves also a composite id class:
#Entity
#IdClass(MyCompositeIdClass.class)
#Inheritance(strategy = InheritanceType.JOINED)
public class MySuperClass {
#Id
private Long myFirstId;
#Id
private Long mySecondId;
}
public class MyCompositeIdClass implements Serializable {
private static long serialVersionUID = 1L;
#Id
private Long myFirstId;
#Id
private Long mySecondId;
}
Therefore, I have an extension of this superclass and it's repository interface.
#Entity
#IdClass(MyCompositeIdClass.class)
public class MySubClass extends MySuperClass {
private String myAttr;
}
#Repository
public interface MySubClassRepository extends JpaRepository<MySubClass, MyCompositeIdClass> {
}
And when executing findAll() repository interface method, Hibernate is executing the join query with the wrong fields comparison on the join clause (inverting both PK columns):
select
mysuperc0_.my_first_id
mysuperc0_.my_second_id
mysuperc0_1_.my_attr
from
my_super_class mysuperc0_
inner join
my_sub_class mysuperc_0_1_
on
my_superc0_.my_first_id = mysuperc_0_1_.my_second_id
and
my_superc0_.my_second_id = mysuperc_0_1_.my_first_id
Was it configuration issues or a framework internal problem? Tried to research about inheritance issues with composite id, but got only opened issues ran from exceptions, but, in this case, I do not have exceptions, it just returns an empty array.
Related
We're using Hibernate to generate JPA Metamodel - Classes for our Entities.
That's working quite fine for most cases, but if there's a relation (#ManyToOne) to an Entity in an #Embeddable, there's no SingularAttribute generated.
Classes are implemented like this (following a "Generation Gap Pattern"):
#Entity
public class EntityA extends EntityABase {
....
}
#MappedSuperClass
public abstract class EntityABase {
#EmbeddedId
private EntityAPrimaryKey primaryKey;
}
#Embeddable
public class EntityAPrimaryKey extends EntityAPrimaryKeyBase {
...
}
#MappedSuperClass
public class EntityAPrimaryKeyBase {
#ManyToOne
#NotNull
private EntityB entityB;
private String someText;
}
Result is like this
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(EntityAPrimaryKeyBase.class)
public abstract class EntityAPrimaryKeyBase_ {
public static volatile SingularAttribute<EntityAPrimaryKeyBase, String> someText;
}
So, the "ordinary" field someText is generated fine, but the Attribute for the relationship to EntityB is missing.
The expected output would be
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(EntityAPrimaryKeyBase.class)
public abstract class EntityAPrimaryKeyBase_ {
public static volatile SingularAttribute<EntityAPrimaryKeyBase, String> someText;
public static volatile SingularAttribute<EntityAPrimaryKeyBase, EntityB> entityB;
}
All other Metamodel-Classes are generated fine (EntityB, EntityA, EntityABase etc.)
I've tried removing the indirection between EntityAPrimaryKey and EntityAPrimaryKeyBase (and annotating the EntityAPrimaryKeyBase with #Embeddable), but that doesn't change the output.
Any ideas why the Attribute entityB isn't generated? Would be very helpful!
JPA Spec (11.1.17) - EmbeddedId Annotation
The EmbeddedId annotation is applied to a persistent field or property of an entity class or mapped superclass to denote a composite primary key that is an embeddable class. The embeddable class must be annotated as Embeddable.[104] Relationship mappings defined within an embedded id class are not supported.
JPA Spec (2.11.2) - Mapped Superclasses
A class designated as a mapped superclass can be mapped in the same way as an entity except that the mappings will apply only to its subclasses since no table exists for the mapped superclass itself.
You cannot have a relationship mapping within an #Embeddable that is to be used as an #EmbeddedId. Even tho you have the relationship in the #MappedSuperclass, 2.11.2 states that mappings are applied to the subclasses, which in this case is the #Embeddable.
For the record, changing the class structure too (and thus making it according to the spec) solves the Problem:
#Entity
public class EntityA extends EntityABase {
....
}
#MappedSuperClass
public abstract class EntityABase {
#EmbeddedId
private EntityAPrimaryKey primaryKey;
#ManyToOne
#MapsId("entityBID")
private EntityB entityB;
}
#Embeddable
public class EntityAPrimaryKey extends EntityAPrimaryKeyBase {
...
}
#MappedSuperClass
public class EntityAPrimaryKeyBase {
private Long entityBID;
private String someText;
}
I am creating entities that are the same for two different tables. In order do table mappings etc. different for the two entities but only have the rest of the code in one place - an abstract superclass. The best thing would be to be able to annotate generic stuff such as column names (since the will be identical) in the super class but that does not work because JPA annotations are not inherited by child classes. Here is an example:
public abstract class MyAbstractEntity {
#Column(name="PROPERTY") //This will not be inherited and is therefore useless here
protected String property;
public String getProperty() {
return this.property;
}
//setters, hashCode, equals etc. methods
}
Which I would like to inherit and only specify the child-specific stuff, like annotations:
#Entity
#Table(name="MY_ENTITY_TABLE")
public class MyEntity extends MyAbstractEntity {
//This will not work since this field does not override the super class field, thus the setters and getters break.
#Column(name="PROPERTY")
protected String property;
}
Any ideas or will I have to create fields, getters and setters in the child classes?
Thanks,
Kris
You might want to annotate MyAbstractEntity with #MappedSuperclass class so that hibernate will import the configuration of MyAbstractEntity in the child and you won't have to override the field, just use the parent's. That annotation is the signal to hibernate that it has to examine the parent class too. Otherwise it assumes it can ignore it.
Here is an example with some explanations that may help.
#MappedSuperclass:
Is a convenience class
Is used to store shared state & behavior available to child classes
Is not persistable
Only child classes are persistable
#Inheritance specifies one of three mapping strategies:
Single-Table
Joined
Table per Class
#DiscriminatorColumn is used to define which column will be used to distinguish between child objects.
#DiscriminatorValue is used to specify a value that is used to distinguish a child object.
The following code results in the following:
You can see that the id field is in both tables, but is only specified in the AbstractEntityId #MappedSuperclass.
Also, the #DisciminatorColumn is shown as PARTY_TYPE in the Party table.
The #DiscriminatorValue is shown as Person as a record in the PARTY_TYPE column of the Party table.
Very importantly, the AbstractEntityId class does not get persisted at all.
I have not specified #Column annotations and instead are just relying on the default values.
If you added an Organisation entity that extended Party and if that was persisted next, then the Party table would have:
id = 2
PARTY_TYPE = "Organisation"
The Organisation table first entry would have:
id = 2
other attribute value associated specifically with organisations
#MappedSuperclass
#SequenceGenerator(name = "sequenceGenerator",
initialValue = 1, allocationSize = 1)
public class AbstractEntityId implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "sequenceGenerator")
protected Long id;
public AbstractEntityId() {}
public Long getId() {
return id;
}
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "PARTY_TYPE",
discriminatorType = DiscriminatorType.STRING)
public class Party extends AbstractEntityId {
public Party() {}
}
#Entity
#DiscriminatorValue("Person")
public class Person extends Party {
private String givenName;
private String familyName;
private String preferredName;
#Temporal(TemporalType.DATE)
private Date dateOfBirth;
private String gender;
public Person() {}
// getter & setters etc.
}
Hope this helps :)
Mark the superclass as
#MappedSuperclass
and remove the property from the child class.
Annotating your base class with #MappedSuperclass should do exactly what you want.
This is old, but I recently dealt with this and would like to share my solution. You can add annotations to an overridden getter.
#MappedSuperclass
public abstract class AbstractEntity<ID extends Serializable> implements Serializable {
#Column(name = "id", nullable = false, updatable = false)
#Id
private ID id;
public ID getId() {
return id;
}
...
}
#Entity
#Table(name = "address")
public final class Address extends AbstractEntity<UUID> implements Serializable {
...
#Override
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
public final UUID getId() {
return super.getId();
}
...
}
I have an entity that is super class
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "super_class")
public abstract class SuperClass implements Serializable {
#Transient
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private long id;
public abstract void initDefaultValues();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
and some subclasses that extend the SuperClass.
#Entity
#Table(name = "Subclass1")
public class Subclass1 extends SuperClass{
private static final Logger log = LogManager
.getLogger(Subclass1.class);
#Transient
private static final long serialVersionUID = 1L;
// testcase configuration tab
private String configurationTabTestServer;
private String umtsRelease;
}
The other classes look the same.
I used to have them SINGLE_TABLE for inheritance type but we wanted each concrete class to have each own table. Because of TABLE_PER_CLASS I had to use GenerationType.TABLE.
I also have an entity class that has a foreign key to the super class
#Entity
#Table(name="myother_entity")
class Entity1{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToOne(cascade = CascadeType.ALL)
private SuperClass superclass;
//more fields
}
I used an abstract class because I have one Entity1 class that could have different type of Superclass. We didn't want to create different Entity1 and Entity2 and Entity3 etc classes for each subclass. So we created one Entity1 class that can have a field of type SuperClass, that could point to any of the subclasses.
On my program I create many Entity1 intances that some of them that have different type of superclass as field value. Each could be of type subclass1 or subclass2 etc. At first we used to have one single table for all subclasses. Everything worked fine. But after we decided to split our tables this is what it happens. When I edit any Entity1 instance, that has already SuperClass field set(using one of the sub classes), and save it (merging it) then it creates a new instance of my Subclass associated with my Entity1 instance, and then saves it to the database. So I have two records now on the table of the subclass. This didn't happen when we used a SINGLE_TABLE inheritance type. Is this normal behaviour for JPA and hibernate?
Please, first consider this: DiscriminatorColumn and DiscriminatorValue annotations are specific to single-table approach. So they aren't to be used in table-per-class mappings.
Now, let's go to the issue:
In table-per-class mapping, there will be two records with same ID: one in a parent table, other in a child table.
As I understood, in your case, two records are being written in the child table, right? If so, the problem must be when you load the Entity1 data from the database. The property "superclass" must have its ID set. You can use eager or lazy loading for this. And check if that property is properly loaded (in debug mode) with its correct ID set before saving it.
Another way is to disable "cascade persist/merge" and to save the entities separately. It can provide more security to your data.
You can find more information here: http://docs.oracle.com/javaee/6/tutorial/doc/bnbqn.html
I have a simple Java EE 7 Web App with Eclipselink and the TABLE_PER_CLASS inheritance strategy.
Following classes:
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Entity
public abstract class AbstractService implements Serializable {
private static final long serialVersionUID = 7041207658121699813L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn
private PersonGroup personGroup;
}
#Entity
public class Service extends AbstractService implements Serializable {
private static final long serialVersionUID = 3817106074951672799L;
}
#Entity
public class PersonGroup implements Serializable {
private static final long serialVersionUID = 3205092801888510996L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "personGroup")
private List<AbstractService> services;
}
In persistence.xml I do Drop&Create.
After creating the database, I have this tables:
abstractservice, service, persongroup
The point is now, that eclipselink creates the table abstractservice with (only(!)) the attribute persongroup_id (no "id" attribute). Why?
My understanding from TABLE_PER_CLASS is, that every attribute and key is going "down", so abstractservice should have no more attributes and should not exist.
My businesscase is, that I have a lot of subservice from AbstractService. I want to get all subservices from AbstractService with a special persongroup.
The AbstractServicetable has no entries, because everything is in Service.
With CriteriaBuilder I say:
Select from AbstractService where persongroup_id = 123;
The Criteria Api should build this (with some union, if more subservices would exist), because I have TABLE_PER_CLASS:
Select from Service where persongroup_id = 123;
Why is eclipselink creating persongroup_id in abstractService and how can I solve my case?
At the end the result of the query is always empty, because abstractService is empty...
Same question was asked here: http://www.eclipse.org/forums/index.php/t/406338/
and seems to be related to bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=265702 which was fixed but regressed. A new bug should be filed for it if you are seeing this in the latest version.
If you are only using a single Servide subclass, you might want to make it a mappedSuperclass instead. If not, a different inheritance type such as joined or single table is usually recommended. This bug seems to only affect DDL generation, so you can switch to have JPA create a script that you can then edit to remove the AbstractService table entries.
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.