When im using table-per-subclass strategy my base (parent class) contains compound primary.
#Column(nullable = false)
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XXX")
#SequenceGenerator(name = "XXX", sequenceName = "XXX", allocationSize = 1)
private Long systemId;
#Id
#Column(nullable = false)
private Long version;
So, all entities that extend this (as i got it) inherit ids and its annotated properties.
The problem, is that when i create a new Parent entity, the primary is being created from sequence, thats ok. But when i create subclass instance it increments my parent id i dont need (creates a new primary from seq.), `coz i use table-per-subclass and primaries gottta be the same.
Question: how to supress id generation in Subclasses?
#Entity
#PrimaryKeyJoinColumns(
{
#PrimaryKeyJoinColumn(name = "systemId"),
#PrimaryKeyJoinColumn(name = "version")
}
)
public class SUb extends Parent {
public SUb (Parent t) {
super(t);
}
public SUb () {
}
...
... no ids...
When the Subclass is created it will issue two inserts:
one in the base class
one in the sub class
The subclass doesn't inherit the #Ids, but instead it uses the:
systemId
version
as FK to the associated columns of the Base table.
So while the base class has a primary key composed of those two columns:
a sequence generated systemId
a manually assigned version
The subclass should have:
a sequnceId column with a FK to the base sequnceId column
a version column with a FK to the base version column
So the sequence shouldn't be called twice while inserting a sub class entity.
Can you confirm that the database tables are following this design?
I couldn't manage to reproduce your problem. Can you be more specific on what is your join strategy? Is it InheritanceType.TABLE_PER_CLASS or InheritanceType.JOINED? Nonetheless I managed to prepare complete example with TABLE-PER-CLASS join strategy which does increment the sequence only once when you persist SubDepartment. Maybe it helps.
#Entity
#IdClass(CompoundPK.class)
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Department {
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XXX")
#SequenceGenerator(name = "XXX", sequenceName = "XXX", allocationSize = 1)
#Id
private Long id;
#Id
private Long version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
}
#Entity
public class SubDepartment extends Department {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class CompoundPK implements Serializable {
private Long id;
private Long version;
public CompoundPK() {
}
public CompoundPK(Long id, Long version) {
this.id = id;
this.version = version;
}
public Long getId() {
return id;
}
public Long getVersion() {
return version;
}
// hashCode & equals
}
Related
I have this entity, called FatRabbitCarrot:
#Entity
public class FatRabbitCarrot {
private Long id;
private FatRabbit fatRabbit;
private Carrot carrot;
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToOne
#JoinColumn(name = "fatRabbit_id", foreignKey = #ForeignKey(name = "FK_FatRabbitCarrot_fatRabbit"))
public FatRabbit getFatRabbit() {
return fatRabbit;
}
public void setFatRabbit(FatRabbit fatRabbit) {
this.fatRabbit = fatRabbit;
}
#ManyToOne
#JoinColumn(name = "carrot_id", foreignKey = #ForeignKey(name = "FK_FatRabbitCarrot_carrot"))
public Carrot getCarrot() {
return carrot;
}
public void setCarrot(Carrot carrot) {
this.carrot = carrot;
}
}
And it works. Now the above class had field names replaced, but the structure is the same as in our repository.
Then I tried to add a new entity, that has a foreign key to the class above. Let's call this class NutToffee. FatRabbitCarrot have a OneToMany relationship to this new entity, while the entity itself should have a ManyToOne relationship:
#Entity
public class NutToffee {
private Long id;
private String text;
private FatRabbitCarrot fatRabbitCarrot;
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(Long id){
this.id = id;
}
#Basic
#Column(name = "text")
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
#ManyToOne
#JoinColumn(name="fatRabbitCarrot_id", foreignKey = #ForeignKey(name = "FK_NutToffee_fatRabbitCarrot"))
public FatRabbitCarrot getFatRabbitCarrot() {
return fatRabbitCarrot;
}
public void setFatRabbitCarrot(FatRabbitCarrot fatRabbitCarrot) {
this.fatRabbitCarrot = fatRabbitCarrot;
}
}
Now this seems like a valid class to me. But it doesn't look like it is. We are using Java 8, Hibernate JPA 2.1, Java EE 7 and gradle to build the artifact we want to deploy. We attempt to deploy it on a Wildfly 10 application server, but we get the following error:
[2019-07-08 03:53:45,441] Artifact Gradle : com.solveralynx.wildrunner : fatties.war: java.lang.Exception: {"WFLYCTL0080: Failed services" => {"jboss.persistenceunit.\"fatties.war#FatUnit\"" => "org.jboss.msc.service.StartException in service jboss.persistenceunit.\"fatties.war#FatUnit\": org.hibernate.MappingException: Foreign key (FK_NutToffee_fatRabbitCarrot:NutToffee [fatRabbitCarrot_id])) must have same number of columns as the referenced primary key (FatRabbitCarrot [fatRabbit_id,carrot_id])
Caused by: org.hibernate.MappingException: Foreign key (FK_NutToffee_fatRabbitCarrot:NutToffee [fatRabbitCarrot_id])) must have same number of columns as the referenced primary key (FatRabbitCarrot [fatRabbit_id,carrot_id])"},"WFLYCTL0412: Required services that are not installed:" => ["jboss.persistenceunit.\"fatties.war#FatUnit\""],"WFLYCTL0180: Services with missing/unavailable dependencies" => undefined}
From my understanding, Hibernate found a composite primary key for FatRabbitCarrot? Even though we never defined one? It seems to pick up a fake primary key, where it uses both foreign keys from entity FatRabbitCarrot.
As for my testing. I am confident this is a Hibernate issue. No matter the database state, I always get this error. I tested with various parameters on the getters, that connect that entities, but no success. If I remove both new OneToMany and ManyToOne connections, the artifact deploys.
Does anyone have any idea why Hibernate is doing this?
You are using #JoinColumn annotation incorrectly.
#Entity
public class FatRabbitCarrot {
#Id
#GeneratedValue
private Long id;
#OnToMany
private List<NutToffee> toffies;
}
#Entity
public class NutToffee {
#Id
#GeneratedValue
private Long id;
#ManyToOne
#JoinColumn(name = "fatRabbitCarrot_id")
private FatRabbitCarrot fatRabbitCarrot;
}
This means that you will have association between FatRabbitCarrot and NutToffee using a join table. An you will have an additional fatRabbitCarrot_id column in the NutToffee table.
You need to use mappedBy
#Entity
public class FatRabbitCarrot {
#Id
#GeneratedValue
private Long id;
#OnToMany(mappedBy = "fatRabbitCarrot")
private List<NutToffee> toffies;
}
#Entity
public class NutToffee {
#Id
#GeneratedValue
private Long id;
#ManyToOne
#JoinColumn(name = "fatRabbitCarrot_id")
private FatRabbitCarrot fatRabbitCarrot;
}
if you don't need the #ManyToOne association, you can use #JoinColumn with #OneToMany without mappedBy
#Entity
public class FatRabbitCarrot {
#Id
#GeneratedValue
private Long id;
#OnToMany
#JoinColumn(name = "fatRabbitCarrot_id")
private List<NutToffee> toffies;
}
#Entity
public class NutToffee {
#Id
#GeneratedValue
private Long id;
}
https://stackoverflow.com/a/37542849/3405171
This question already has answers here:
Hibernate : How override an attribute from mapped super class
(2 answers)
Closed 5 years ago.
I have a problem for #Id in JPA that is described as follow?
There is a generic class as follow?
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
private static final long serialVersionUID = 4295229462159851306L;
private T id;
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
}
There is another class that extends from it as follow?
#Entity
#Table(name = "DOC_CHANGE_CODE" )
public class ChangeCode extends BaseEntity<Long> {
#Id
#GeneratedValue(generator = "sequence_db", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "sequence_db", sequenceName = "SEQ_DOC_CHANGE_CODE", allocationSize = 1)
public Long getId () {
return super.getId();
}
}
Because any sub class has its own sequence, I must specific any subclass #Id, because of that I override the its getter and put some annotations in top of that. Unfortunately it does not work correctly.
How do I fix problem and get my goal?
Try this:
#Entity
#Table(name = "DOC_CHANGE_CODE" )
#SequenceGenerator(name = "sequence_db", sequenceName = "SEQ_DOC_CHANGE_CODE", allocationSize = 1)
#AttributeOverride(name = "id", column = #Column(name = "ID"))
public class ChangeCode extends BaseEntity<Long> {
#Override
#Id
#GeneratedValue(generator = "sequence_db", strategy = GenerationType.SEQUENCE)
public Long getId() {
return id;
}
}
It's not possible to override the #Id from a base class.
The only way to do it is to provide your own custom IentifierGenerator and provide a different logic based on the subclass (e.g. using a sequence name based on the subclass name).
That's why adding the #Id attribute in the #MappedSuperclass only makes sense for the assigned generator or for IDENTITY.
I'm trying to use objects as #Id but Hibernate doesn't seem to convert the object to the corresponding primitive type stated using an AttributeConverter.
#Entity
#Table(name = "ris_exam")
public class Exam {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long examId;
}
public class ExamId {
private Long id;
public ExamId(final Long id) {
super();
this.id = id;
}
public Long getId() {
return this.id;
}
}
#Converter(autoApply = true)
public class ExamIdCodeAttributeConverter implements AttributeConverter<ExamId, Long> {
#Override
public Long convertToDatabaseColumn(ExamId arg0) {
return arg0.getId();
}
#Override
public ExamId convertToEntityAttribute(Long arg0) {
return new ExamId(arg0);
}
}
Hibernate does this query when creating the table
create table ris_exam (id varbinary(255) identity not null, ...)
If you want to play around with wrapping of the basic column then you must take into consideration that most likely you will not be able to use the #GeneratedValue annotation on your id (the same goes for using the #EmbeddedId which is more suitable than the converter in my opinion anyway).
If you need to anyway then in my opinion you could try the #IdClass feature but remember that the ExamId class would only be used while specifiying the queries / retrieving specific entity:
#Entity
#Table(name = "ris_exam")
#IdClass(ExamId.class)
public class Exam {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long examId;
}
in DAO:
ExamId id = new ExamId(1L);
session.get(Exam.class, id);
when retrieved, the entities would contain plain long types.
Just an option for you to consider..
I have this error in my eclipse editor This class has a composite primary key. It must use an ID class. , but the class does not has a composite primary key, because the id is a Long
#SuppressWarnings("serial")
#Entity
#Table(name = "T_PRODUCT")
#SequenceGenerator(name = "seqPRODUCT", sequenceName = "SEQ_PRODUCT")
public class Product extends ItemBase implements java.io.Serializable {
#Id
private Long id;
#Id
#Column(name = "ID", unique = true, nullable = false, precision = 38, scale = 0)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqPRODUCT")
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
..
}
Hibernate is seeing both annotations on the property and on the getter-method and therefore assumes that you are using a composite key.
I have a one to many relationship between Father and Son. I am working with JPA Repository.
If I delete a son with id 54 and then get all the sons again :
sonRepository.delete(id);
List<Son> sons = sonRepository.findAll();
I am getting this error :
javax.persistence.EntityNotFoundException: Unable to find
com.myapp.domain.Son with id 54
My entities :
#Entity
#Table(name = "T_FATHER")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class FAther implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "father", fetch = FetchType.EAGER)
#JsonIgnore
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Son> sons = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Set<Son> getSons() {
return sons;
}
public void setSons(Set<Son> sons) {
this.sons = sons;
}
}
#Entity
#Table(name = "T_SON")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Son implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
private Father father;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Father getFather() {
return father;
}
public void setFather(Father father) {
this.father = father;
}
}
This error seems to happen only when the son is already in database or inserted in database via sql (not with the application). If the son is added via the application :
sonRepository.save(son);
I don't have any problem. The problem happens again if I restart the server. It 's like the problem appears if the addSon and deleteSon are not done in the same session (same server instance).
I don't understand what I am doing wrong. If someone can help me on this...
Thank you.
It seems a bug to me in the caching mechanism. I would report it to Spring. As a working workaround the OP confirmed the removing #Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE).