Here is a basic drawing for my problem.
So I have a Person class.
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#Column(name = "person_id")
#SequenceGenerator(sequenceName = "PERSON_ID_SEQ", allocationSize = 1, name="PERSON_ID_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PERSON_ID_SEQ")
private Long personId;
private String name;
}
and for example a Police class that is inherited from a Person.
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "POLICE_MAN")
public class PoliceMan extends Person{
#Id
#Column(name = "police_id")
private Long policeId;
private String something;
}
So the problem is, I can't use #Id annotation in PoliceMan because I have one in the Person class. How could I solve that? How can a police_id be an uniqu identifier?
Thanks for help.
https://www.baeldung.com/hibernate-inheritance
I tried something from that page, but ...
You missed the Inheritance mapping. Just extending the Person class is not a JPA inheritance. By looking at your code I suppose that you wanted to make a joined table inheritance, because you tried to map each entity to a different table.
To do so you have to use the #Inheritance(strategy = InheritanceType.JOINED) annotation on the PoliceMan class.
Also you should use the #PrimaryKeyJoinColumn(referencedColumnName = "person_id") annotation on the PoliceMan class. This indicates that the PK of the PoliceMan entity is person_id and it has a foreign key constraint to the PK of the Person. Remove the policeId field. With the annotations above you already have the person_id column in both of your tables (you don't have to declare an id in your PoliceMan class).
So if you do it manually on SQL side you have to create a person_id primary key column in PERSON table and also in POLICE_MAN table, and you should have a foreign key in POLICE_MAN table on person_id column that references the person_id column of the PERSON table.
"It might be more accurate to associate it with a foreign key. You can establish a One To One relationship between Person and PoliceMan."
"our table structure doesn't show a FK from police->person. Nothing stops you from having a police_id attribute within your entity, it just can't be marked as THE id. You can have any number of unique identifiers within entities and tables - just mark the column with a uniqueness constraint. Plus one on this shouldn't be using inheritance despite that though. An officer (PoliceMan) is a role, not a defining characteristic of a person, and you'll have issues maintaining their existence as a person if it isn't separated out, just as you apparently are for the table data."
Thanks you all have right.
Related
Situation: Our application has been working properly with all the OneToMany associations, with the names, columns. We decided out of nowhere that we want our entity objects to change names just by adding DAO at the end. Object -> ObjectDAO.
What we did: We changed object names as planned. Then every entity got its #Table(name = "object") annotation, but now we are running into problems on our join tables. The names of columns are now generated badly - not using the given table's name. We want the column names to remain object_id instead of objectdao_id but #Table annotation does not do the trick.
nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing column [great_objectdao_id] in table [our_join_table]
I suppose column name generation was always taking into consideration not a table name, but object name then. What's making things harder, is our scheme where we have a BaseDAO and BaseObjectDAO objects inside of which the #OneToMany relations exist.
SomeOtherDAO object would have a some_other table name.
#Data
#MappedSuperclass
public abstract class BaseDAO {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private Long id;
(...)
}
#EqualsAndHashCode(callSuper = true, exclude = {"objectSources"})
#NoArgsConstructor
#Data
#MappedSuperclass
public abstract class BaseObjectDAO extends BaseDAO {
#JsonIgnore
#OneToMany
private Set<SomeOtherDAO> objectSources;
(........)
}
How do the objects extending BaseObjectDAO look like:
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "great_object")
#Data
#NoArgsConstructor
#Accessors(chain = true)
public class GreatObjectDAO extends BaseObjectDAO {
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "strange_object")
#Data
#NoArgsConstructor
#Accessors(chain = true)
public class StrangeObjectDAO extends BaseObjectDAO {
Question:
How do we force it to generate join tables with column names great_object_id and strange_object_id instead of great_objectdao_id and strange_objectdao_id?
The great_object table should remain to have the id column, and the change should only be visible in the join table.
join_table table column names we have and want to keep:
great_object_id, object_sources_id
You must use the #AttributeOverride annotation to change the name of the id column of the subclasses. See https://docs.oracle.com/javaee/6/api/javax/persistence/AttributeOverride.html
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "great_object")
#Data
#NoArgsConstructor
#Accessors(chain = true)
#AttributeOverride(name="id", column=#Column(name="great_object_id"))
public class GreatObjectDAO extends BaseObjectDAO {
I have below Hibernate relations in a spring boot application.
#Entity
#Table(name = "projects")
public class Project {
#Embedded
private ProjectParameters parameters;
}
#Embeddable
public class ProjectParameters {
#Column(name = "hvacConfigs")
#ElementCollection(targetClass = HVACUserConfigModel.class)
#CollectionTable(uniqueConstraints = #UniqueConstraint(columnNames = {}), foreignKey = ForeignKey(name = "project_hvacConfig_fk"))
private Set<HVACUserConfigModel> hvacConfigs;
}
#Entity
#Table(name = "hvacuserconfig")
public class HVACUserConfigModel {
#Id
#GeneratedValue
private Integer id;
}
Mysql table auto-generated is as follows. Please open images in these below links as I don't have 10 reputation to post images on stackoverflow.
The issue is I am able to save multiple projects with same project like below
Project_uid | hvacConfigs_id
------------------------------
1001 | 1
1001 | 2
Adding (1002 | 1) throws unique constraint violation.
I am able to get it working by changing table definition explicitly using mysql workbench by removing the unique constrain UK_me0ekntab0gknshag0xjjv35x. To remove unique constrain I have to first remove foreign key constrain FKeygabcnr2stdchxqpb5wuinf2 which is being referred by unique constrain. After these changes, I from spring boot app, I insert new rows it is successfully added with values (1002 | 1) & (1002 | 2).
I am able to explore that #CollectionTable annotation, annotated in hvacConfigs field in Java code, provides ways to insert uniqueConstraint and foreignKey definition as part of attribute definition. I have tried tweaking these attributes of annotation but with no success.
Please let me know if you have any solution how to override the unique constraint and foreign key constraints using java code so that this works without any manual adjustment to mysql table explictly.
To be honest, this is unclear what you want to achieve. But your mapping is invalid. You can use #ElementCollection annotation only with basic or embeddable types. If you want operate with HVACUserConfigModel as with an entity you should use #OneToMany association.
So, you can correct your mapping in this way:
#Embeddable
public class ProjectParameters {
#ElementCollection
#CollectionTable(
name = "hvacuserconfig",
joinColumns = #JoinColumn(name = "hvacConfigs_id")
)
private Set<HVACUserConfigModel> hvacConfigs;
}
#Embeddable
public class HVACUserConfigModel {
// #Id annotation is not used with #Embeddable types
// Like basic types, embeddable types do not have any identity,
// being managed by their owning entity.
private Integer id;
// ...
}
or in this way:
#Embeddable
public class ProjectParameters {
#OneToMany
#JoinColumn(name = "hvacConfigs_id")
private Set<HVACUserConfigModel> hvacConfigs;
}
#Entity
#Table(name = "hvacuserconfig")
public class HVACUserConfigModel {
#Id
#GeneratedValue
private Integer id;
}
We solved this using ManytoMany annotation instead of ElementCollection annotation. It works the same way creating new intermediate table to contain Project Id and HVACUserConfigModel id but does not create the unique index.
#MappedSuperClass
public abstract class BaseMappedSuperClass {
#EmbeddedId
private EmbeddedId id;
}
#Entity
#Multitenant(MultitenantType.TABLE_PER_TENANT)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "typeCol")
public abstract class Vehicle extends BaseMappedSuperClass{
private String name;
}
#Entity(name = "Cycle")
#Multitenant(MultitenantType.TABLE_PER_TENANT)
#DiscriminatorValue(value = "Cycle")
public class Cycle extends Vehicle {
private String bellType;
}
#Entity(name = "Bus")
#Multitenant(MultitenantType.TABLE_PER_TENANT)
#DiscriminatorValue(value = "Bus")
public class Bus extends Vehicle {
private String gearType;
}
I have the above entity structure and if I try to do an insert op on the entity Cycle or Bus, it fails inconsistently, because of the missing primary key field (id).
When I tried to debug the JPA codebase, I figured that the tenant discriminator, which is tenant_id in my case is not appended to the table name prefix for the embeddedId field 'Id' and the discriminator column field 'typeCol'.
What is more interesting is that this behavior is not consistent. If I restart my application and try, it works. If I restart again and try,it does not work.
Any help would be appreciated. Version of eclipse link used is 2.5.1.
What is the logic behind the order in which the entities are processed to initialize the metadata?
I have a group of tables, that are all identical apart from their owner table, and the corresponding foreign keys to that table. I made it all generic thanks to Hibernate/JPA, but cannot pass the #JoinColumn information via #AssociationOverride since the name value for it is ignored, or not overridden at all.
For example;
#Data
#Entity
#Table(name = "etc")
#AssociationOverride(name = "parent", joinColumns = #JoinColumn(name = "id"))
public class RealEntity extends BaseEntity<ParentEntity, String> {
}
with;
#Data
#MappedSuperClass
public class BaseEntity<K, P> implements Serializable {
#EmbeddedId
protected Key<K> key = new Key<>();
#MapsId("fk")
#ManyToOne
#JsonBackReference
protected P parent;
#Data
#Embeddable
public static class Key<K> implements Serializable {
protected K fk;
#Column(name = "sub_id")
protected String subId;
}
}
parent:
#Data
#Entity
#Table(name = "parentc")
public class ParentEntity implements Serializable {
#Column(name = "id")
protected String parentId;
}
So as you see, it works well except for the parent's fk reference definition, I get mapping error since Hibernate tries to find a parent_id, rather than just id for the foreignKey, since #JoinColumn override is ignored. It works if I put the parent information in the RealEntity directly (obviously), or if I put #JoinColumn(name = "id") on parent in BaseEntity but I want to keep it as generic as possible. Is there any solution to this issue? Or should I just give up?
edit: it seems when I put a proper #JoinColumn with acceptable mapping for joining on parent in BaseEntity, that does get overridden, so it needs something valid to override. I cannot just add an association from nothingness is that the case? I've seen many examples on the web where they were putting associations from scratch, my usage of #MapsId, might be breaking the usage I guess. But I cannot change my current structure, since it is necessary to be able to represent composite foreign key definition for dependent child tables... I feel like there is a very simple solution, or some hacky way to achieve what I want, and I cannot seem to find it!
My data model represents legal entities, such as a Business or a Person. Both are tax-paying entities, and both have a TaxID, a collection of phone numbers, and a collection of mailing addresses.
I have a Java model with two concrete classes that extend an abstract class. The abstract class has properties and collections that are common to both concrete classes.
AbstractLegalEntity ConcreteBusinessEntity ConcretePersonEntity
------------------- ---------------------- --------------------
Set<Phone> phones String name String first
Set<Address> addresses BusinessType type String last
String taxId String middle
Address Phone
------- -----
AbsractLegalEntity owner AbstractLegalEntity owner
String street1 String number
String street2
String city
String state
String zip
I'm using Hibernate JPA Annotations on a MySQL database, with classes like this:
#MappedSuperclass
public abstract class AbstractLegalEntity {
private Long id; // Getter annotated with #Id #Generated
private Set<Phone> phones = new HashSet<Phone>(); // #OneToMany
private Set<Address> address = new HashSet<Address>(); // #OneToMany
private String taxId;
}
#Entity
public class ConcretePersonEntity extends AbstractLegalEntity {
private String first;
private String last;
private String middle;
}
#Entity
public class Phone {
private AbstractLegalEntity owner; // Getter annotated #ManyToOne #JoinColumn
private Long id;
private String number;
}
The problem is that Phone and Address objects need to refer to their owner, which is an AbstractLegalEntity. Hibernate complains:
#OneToOne or #ManyToOne on Phone references an unknown
entity: AbstractLegalEntity
It seems like this would be a fairly common Java inheritance scenario, so I hope that Hibernate would support it. I've tried changing the mapping for AbstractLegalEntity based on a Hibernate forum question, no longer using #MappedSuperclass:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
However, now I get the following error. When reading up on this inheritance mapping type, it looks like I have to use SEQUENCE not IDENTITY, and MySQL doesn't support SEQUENCE.
Cannot use identity column key generation with <union-subclass>
mapping for: ConcreteBusinessEntity
I'm making more progress toward getting things working when I use the following mapping.
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name="entitytype",
discriminatorType=DiscriminatorType.STRING
)
I'm thinking I should continue down this path. My concern is that I'm mapping it as an #Entity when I really don't ever want an instance of AbstractLegalEntity to ever exist. I'd like to know if this is the right approach. What is the correct approach I should be taking for this situation?
Use:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
AbstractLegalEntity
In the database you will have one table for AbstractLegalEntity, and tables for classes, which extend AbstractLegalEntity class. You won't have instances of AbstractLegalEntity if it's abstract. Polymorphism can be used here.
When you use:
#MappedSuperclass
AbstractLegalEntity
#Entity
ConcretePersonEntity extends AbstractLegalEntity
This will create only one table in your database called ConcretePersonEntity, containing columns from both classes.
Add #Entity annotation to AbstractLegalEntity. Instance of AbstractLegalEntity will never exist - hibernate will load appropriate extending instances - ConcreteBusinessEntity or ConcretePersonEntity according to Id field.
You have to declare AbstracLegalEntity as an #Entity. Even with the #Entity annotation, your class remains abstract. consequently, you will only have instance of concrete subclasses.