Exception caused by one-to-many bidirectional relationship - java

I have a two entities relationship, I believe that I did all fine, but when I try to run my application, Payara is launching this error:
Caused by: Exception [EclipseLink-7154] (Eclipse Persistence Services - 2.7.4.payara-p2): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [retenciones] in entity class [class komp.model.ChequePropio] has a mappedBy value of [chequepropio] which does not exist in its owning entity class [class komp.model.RetencionChp]. If the owning entity class is a #MappedSuperclass, this is invalid, and your attribute should reference the correct subclass.
Here one of entities causing the problem:
#Entity
#Table(name = "chequepropio", uniqueConstraints = {
#UniqueConstraint(name = "ukchequepropio", columnNames = {"idbanco", "idempresa", "idtipo", "numero"})})
public class ChequePropio implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#OneToMany(fetch = FetchType.LAZY,mappedBy = "chequepropio")
private List<RetencionChp> retenciones;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
The other entities:
#Entity
#Table(name = "retencionchp")
public class RetencionChp implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "idcheque", insertable = true, updatable = true)
private ChequePropio chequePropio;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public ChequePropio getChequePropio() {
return chequePropio;
}
public void setChequePropio(ChequePropio chequePropio) {
this.chequePropio = chequePropio;
}
}
I can't see any error in the relationship, somebody could say me what is wrong?
Thanks in advance!
Fernando

The error is self-explanatory:
The attribute [retenciones] in entity class [class
komp.model.ChequePropio] has a mappedBy value of [chequepropio] which
does not exist in its owning entity class
Mind the keyword chequepropio. You don't have a chequepropio property in RetencionChp. You have a chequePropio instead. Mind the uppercase P. The following should solve it (mappedBy = "chequePropio")).
#Entity
#Table(name = "chequepropio", uniqueConstraints = {
#UniqueConstraint(name = "ukchequepropio", columnNames = {"idbanco", "idempresa", "idtipo", "numero"})})
public class ChequePropio implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#OneToMany(fetch = FetchType.LAZY,mappedBy = "chequePropio")
private List<RetencionChp> retenciones;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}

Related

Understanding Hibernate Junction Table

I have two entities with one to many relationships as below. Everything works fine except delete action. On deleting, I was getting ERROR: relation "a_b" does not exist. For that, I found the solution here.
According to an answer, there was an issue with the relationship and hibernate treats relationships as separate uni-directional relationships and it will create the third table a_b and tracks both sides of the relationship independently. To resolve the issue I had added mappedBy = "a".
Question is
Why does hibernate fires delete query for table a_b while it does not insert into a_b at the time new record creation?
Log on insert
Hibernate: insert into a...
Hibernate: insert into b...
Hibernate: insert into b...
Hibernate: insert into b...
**Why insert into a_b... is not generated/inserted?**
Log on delete
Hibernate: select a0_.id as id1_11_, from a a0_ where (a0_.id in (?))?
Hibernate: delete from b where a_id in (?)
Hibernate: delete from a_b where (a_id) in (select id from a where id in (?))
**Why delete from a_b if nothing is inserted into a_b**
12:19:50.432 [XNIO-1 task-20] WARN o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 0, SQLState: 42P01
12:19:50.433 [XNIO-1 task-20] ERROR o.h.e.jdbc.spi.SqlExceptionHelper - ERROR: relation "a_b" does not exist
with cause = 'org.hibernate.exception.SQLGrammarException: could not execute statement' and exception = 'could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute statement'
Entity A
#Entity
#Table(name = "a")
public class A extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#OneToMany
private List<B> b;
.....
}
Entity B
#Entity
#Table(name = "b")
public class B extends AbstractAuditingEntity implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#ManyToOne
private A a;
.....
}
AServiceImpl
#Override
public int delete(List<Long> ids) {
...
bRepository.deleteWithIds(ids);
aRepository.deleteWithIds(ids);
}
BRepository
#Transactional
#Modifying
#Query("delete from b x where x.a.id in :ids")
void deleteLogsWithIds(#Param("ids") List<Long> ids);
ARepository
#Modifying
#Transactional
#Query("delete from a x where x.id in :ids")
void deleteJobWithIds(#Param("ids") List<Long> ids);
Current Code
Entity A
#Entity
#Table(name = "a")
public class A extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#OneToMany(mappedBy = "a")
private List<B> b;
.....
}
Entity B
#Entity
#Table(name = "b")
public class B extends AbstractAuditingEntity implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#ManyToOne
private A a;
.....
}
EDIT: Insert sequence
Save Entity A
aRepository.saveAndFlush(a);
Make a call to third party API and based on response set Entity A
for saving Entity B
x.forEach(b-> {
b.setA(aRepository.findById(aId).get());
bRepository.save(b);
});
There can be many scenarios to consider
If you are using a uni-directional oneToMany mapping it will require a join table to save the relationship.Since, a single A entity is associated with multiple B entities and due to its unidirectional nature it does not has a mapping column in B table.enter code here
#Entity
#Table(name = "A")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private int id;
private String stateName;
//This is uni-directional since we donot have a corresponding reference to A in B entity
#OneToMany(cascade = CascadeType.ALL)
List<B> bs = new ArrayList<>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<B> getBs() {
return bs;
}
public void setBs(List<B> bs) {
this.bs = bs;
}
public String getStateName() {
return stateName;
}
public void setStateName(String stateName) {
this.stateName = stateName;
}
}
#Entity
#Table(name="B")
public class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="ID")
private int id;
private String districtName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDistrictName() {
return districtName;
}
public void setDistrictName(String districtName) {
this.districtName = districtName;
}
}
In the above case its uni-directional oneToMany and it will require a join-table.
If you save your entity like this
enter code here
A a= new A();
B b=new B();
B b1=new B();
List<B> bs=new ArrayList<>();
bs.add(b);
bs.add(b1);
aRepository.save(a);
This will save the relationship mapping in join table.
Case 2:- Now if you add the following in the B entity class it will create a foreign-key column to A table. This will be again a unidirection ManyToOne mapping.
enter code here
#ManyToOne()
A a;
If you the following
enter code here
A a =new A();
B b =new B();
b.setA(a);
B b1=new B();
b1.setA(a);
bRepository.save(b);
bRepository.save(b1);
This will not save the relationship in the join table instead it will use the foreign-key which is present in the table B column named A_ID.
Case 3 :- Bidirectional oneToMany
enter code here
#Entity
#Table(name = "A")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private int id;
private String stateName;
#OneToMany(mappedBy = "a", cascade = CascadeType.ALL)
List<B> bs = new ArrayList<>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<B> getBs() {
return bs;
}
public void setBs(List<B> bs) {
this.bs = bs;
}
public void addB(B b) {
b.setA(this);
bs.add(b);
}
public void removeB(B b) {
b.setA(null);
bs.remove(b);
}
public String getStateName() {
return stateName;
}
public void setStateName(String stateName) {
this.stateName = stateName;
}
}
#Entity
#Table(name = "B")
public class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private int id;
private String districtName;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "A_ID")
A a;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
public String getDistrictName() {
return districtName;
}
public void setDistrictName(String districtName) {
this.districtName = districtName;
}
}
The above entity mapping is bi-directional oneToMany and doesn't uses the join-table.

Hibernate MappingException: Foreign key must have same number of columns as the referenced primary key

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

Java JPA Hibernate save not inserting referenced entities

I have the following 2 classes:
#Entity
#Table(name = "TableA")
public class EntityA
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private final Integer id = null;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "BId")
private EntityB b;
public EntityA(EntityB b)
{
this.b = b;
}
}
#Entity
#Table(name = "TableB")
public class EntityB
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private final Integer id = null;
#OneToOne(mappedBy = "b")
private final EntityA a = null;
}
When I do
session.save(new EntityA(new EntityB());
the database only inserts a record in TableA and leaves the column that references TableB as NULL
If I first insert b, then a, it works, but it should work with a single call too.
Other questions/answers mention that the annotations are not correct, but I see no difference between mine and the provided solutions.
I also tried adding the CascadeType.PERSIST on both #OneToOne annotations, but that didnt work either.
In jpa, default Cascade setting is NONE ... thus the entities in relationships (B in your case) is not inserted (persisted) by default ... You need to annotate the relationship with
#OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.PERSIST)
First of all you must delete final keyword from your entities.
Try this one:
#Entity
#Table(name = "TableA")
class EntityA {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private Integer id;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinColumn(name = "BId")
private EntityB b;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public EntityB getB() {
return b;
}
public void setB(EntityB b) {
this.b = b;
}
public EntityA(EntityB b) {
this.b = b;
b.setA(this);
}
}
#Entity
#Table(name = "TableB")
class EntityB {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private Integer id;
#OneToOne(mappedBy = "b")
private EntityA a;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public EntityA getA() {
return a;
}
public void setA(EntityA a) {
this.a = a;
}
}
I am using spring boot, hibernate and H2 database
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext app = SpringApplication.run(DemoApplication.class, args);
ServiceCascade bean = app.getBean(ServiceCascade.class);
bean.save();
}
}
#Service
class ServiceCascade {
#PersistenceContext
private EntityManager entityManager;
#Transactional
public void save() {
EntityA entityA = new EntityA(new EntityB());
entityManager.persist(entityA);
}
}
The following logs show that the two entities are inserted correctly
org.hibernate.SQL : insert into tableb (id) values (null)
org.hibernate.SQL : insert into tablea (id, bid) values (null, ?)
o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [1]

Hibernate #OneToOne is null

I'm trying to get Hibernate #OneToOne annotation working with 2 classes, Hito and Portada. Portada table has the foreign key of Hito, an int attribute called hito.
My entities looks like this:
Hito:
#Entity
#Table(name = "hito")
public class Hito implements Serializable {
//...other attributes
private Portada portada;
//...getters and setters from other attributes
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "hito")
public Portada getPortada(){ return portada;}
public void setPortada(Portada portada){ this.portada = portada;}
}
Portada:
#Entity
#Table(name = "portada")
public class Portada {
//...other attributes
private Hito hito;
//...getters and setters from other attributes
#OneToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "hito")
public Hito getHito() {return hito;}
public void setHito(Hito hito) {this.hito = hito;}
}
When I call hito.getPortada(), I expect a Portada object, but it returns null.
Any suggestions?
I tried to reproduce your problem with code:
#MappedSuperclass
public abstract class BaseEntity {
#Id #GeneratedValue
private Long id;
#Version
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
#Table(name = "portada")
public class Portada extends BaseEntity {
//...other attributes
#OneToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "hito")
private Hito hito;
//...getters and setters from other attributes
public Hito getHito() {return hito;}
public void setHito(Hito hito) {this.hito = hito;}
}
#Entity
#Table(name = "hito")
public class Hito extends BaseEntity implements Serializable {
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "hito")
private Portada portada;
public Portada getPortada(){ return portada;}
public void setPortada(Portada portada){ this.portada = portada;}
}
// app:
Portada p = new Portada();
Hito h = new Hito();
p.setHito(h);
h.setPortada(p);
entityManager.persist(h);
entityManager.flush();
entityManager.clear();
Hito h2 = entityManager.find(Hito.class, h.getId());
System.out.println(h2.getPortada().toString());
tx.commit();
The last find generated sql:
select
hito0_.id as id1_4_0_,
hito0_.version as version2_4_0_,
portada1_.id as id1_7_1_,
portada1_.version as version2_7_1_,
portada1_.hito as hito3_7_1_
from
hito hito0_
left outer join
portada portada1_
on hito0_.id=portada1_.hito
where
hito0_.id=?
Everything worked for me...
EDIT: Only difference is that I like to put mapping attributes on fields instead of properties but it doesn't matter in this problem. Please check if you add both of your classes to persistance.xml or hibernate config.

JPA Repository - one to many - issue with removing one child

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).

Categories

Resources