Parent key not found with #MapsId - spring-boot

I am using Java8, SpringBoot 2.3.7 and JPA with Oracle
There is this legacy table (I cannot touch it, as it is in production with other applications)
TRANSFE (TRA_ID_PK NUMBER, ACCOUNT_ID NUMBER, .... )
CONSTRAINT "TRANSFE_UNIQUE" UNIQUE ("TRA_ID_PK")
In my new application some of the new Transfe will be TransfeIn, with only one field, so I created this table
TRANSFE_IN (ID NUMBER, ....)
CONSTRAINT "TRANSF_IN_PK" PRIMARY KEY ("ID")
Everything works fine with Jpa:
#Entity
#Table(name = "TRANSFE")
public class Transfe implements Serializable {
#Id
#Column(name = "TRA_ID_PK")
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_TRANSFES")
#SequenceGenerator(name="SEQ_SW_TRANSFERENCIAS", sequenceName="SEQ_TRANSFES", allocationSize=1)
private Long id;
#ManyToOne
#JoinColumn(name = "TRA_ID")
private Account account;
#OneToOne(cascade = {CascadeType.ALL})
#PrimaryKeyJoinColumn
private TransfeIn transfeIn;
and
#Entity
#Table(name = "TRANSFE_IN")
public class TransferenciaEntrant {
#Id
private Long id;
#MapsId
#OneToOne(cascade = {CascadeType.ALL}, mappedBy = "transfeIn")
#JoinColumn(name = "id")
private Transfe transfe;
The problem raises when I add this to TRANSFE_IN
CONSTRAINT "TRANSFE_IN_FK" FOREIGN KEY ("ID") REFERENCES "TRANSFE" ("TRA_ID_PK")
So when there is a new TransfeIn to be stored with
accountRepository.save(account)
(Account has a #OneToMany(mappedBy = "account", cascade = CascadeType.ALL) List transfes), I get
SQLIntegrityConstraintViolationException: ORA-02291: integrity constraint (TRANSFE_ENTR_FK) violated - parent key not found
In the logs, I can see how a new TRANSFE_IN row with the right ID (taken from SEQ_TRANSFES) is being inserted. But the table TRANSFE is empty, yet.
What am I doing wrong? Should I not use the FK? Or there is something in the annotations to be changed?

Related

Referential integrity constraint violation with Cascade type ALL

I need your help with the following problem: there is a spring boot project, it has two entities:
Bank and CreditDetails, the bank refers to the details as OneToMany, the details as ManyToOne.
In the Bank entity, the cascade type is ALL, but when I try to delete the bank, I get an error, what could be the problem?
Bank:
#Entity
#Table(name = "banks")
#Getter
#Setter
#NoArgsConstructor
public class Bank {
#Id
#Column(name = "bank_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#OneToMany(orphanRemoval = true,cascade = CascadeType.ALL,
mappedBy = "bank", fetch = FetchType.LAZY)
private List<CreditDetails> creditDetails = new ArrayList<>();
#OneToMany(orphanRemoval = true,cascade = CascadeType.ALL,
mappedBy = "bank", fetch = FetchType.LAZY)
private List<Client> clients = new ArrayList<>();
}
Credit Details:
#Entity
#Table(name = "credit_details")
#Getter
#Setter
#NoArgsConstructor
public class CreditDetails {
#Id
#Column(name = "credit_details_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#Column(name = "credit_limit")
private BigDecimal creditLimit;
#Column(name = "credit_percent")
private BigDecimal creditPercent;
#ManyToOne(targetEntity = Bank.class, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch = FetchType.EAGER)
#JoinColumn(name = "bank_id")
private Bank bank;
#OneToMany(cascade = CascadeType.ALL,
mappedBy = "creditDetails")
List<CreditOffer> creditOffers;
}
Entities in DB(H2)
create table banks
(
bank_id uuid primary key
);
create table credit_details
(
credit_details_id uuid primary key,
credit_limit bigint,
credit_percent numeric(5, 2),
bank_id uuid references banks (bank_id),
primary key (credit_details_id)
);
Stacktrace:
Referential integrity constraint violation: "CONSTRAINT_8: PUBLIC.CREDIT_DETAILS FOREIGN KEY(BANK_ID) REFERENCES PUBLIC.BANKS(BANK_ID) ('ae1ce5c1-b1eb-4ee7-a1a2-63d831b0fd0a')";
I reconstructed your setup using spring, hibernate, an H2 database and a Postgres database. For me everything worked as intended.
To test the entities I used a BankRepository:
public interface BankRepository extends CrudRepository<Bank, UUID> {}
and a very simple RestController:
private final BankRepository bankRepository;
#DeleteMapping
public void removeBank(#RequestParam String uuid) {
bankRepository.deleteById(UUID.fromString(uuid));
}
#PostMapping("/add")
public Bank addBank() {
var bank = new Bank();
var creditDetails = new CreditDetails();
creditDetails.setBank(bank);
bank.setCreditDetails(List.of(creditDetails));
bankRepository.save(bank);
return bank;
}
You might geht the error if you try to delete the Bank entity via some SQL directly or if you manually delete it from your database. Could you set
spring.jpa.show-sql: true
and post the generated JPA Queries? Mine looked like this:
Hibernate: select bank0_.bank_id as bank_id1_1_0_ from banks bank0_ where bank0_.bank_id=?
Hibernate: select creditdeta0_.bank_id as bank_id4_2_0_, creditdeta0_.credit_details_id as credit_d1_2_0_, creditdeta0_.credit_details_id as credit_d1_2_1_, creditdeta0_.bank_id as bank_id4_2_1_, creditdeta0_.credit_limit as credit_l2_2_1_, creditdeta0_.credit_percent as credit_p3_2_1_ from credit_details creditdeta0_ where creditdeta0_.bank_id=?
Hibernate: delete from credit_details where credit_details_id=?
Hibernate: delete from banks where bank_id=?

Spring hibernate JPA One to many mapping:Duplicate key error

I have one to many relationships between the following 2 entities.
#Entity
public class OrderItem {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#OneToOne
#JoinColumn(name="ORDER_PRODUCT_ID")
private Product product;
private Long quantity;
private BigDecimal totalPrice;
#OneToMany
#JoinTable(name="orderItem_productaddition",joinColumns=#JoinColumn(name="orderItem_id"), inverseJoinColumns=#JoinColumn(name="ProductAddition_id"))
private Set<ProductAddition> listOfAdditions=new HashSet<>();
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(unique = true)
private ProductOption productOption;
}
Second Entity
#Entity
public class ProductAddition{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String additionDescription;
private BigDecimal additionPrice;
private BigDecimal additionsPriceForSmall;
private BigDecimal additionsPriceForNormal;
private BigDecimal additionsPriceForFamily;
private BigDecimal additionsPriceForParty;
#ManyToMany(fetch = FetchType.LAZY,mappedBy = "productAdditions")
#JsonBackReference
private Set<Product>product=new HashSet<>();
}
Following is the existing data in the join table order_item_productaddition.
For following insert i get the
Duplicate entry '41' for key 'UK_22djpp3b17x2x1vurh26crns'
insert into website_dev.order_item_productaddition values(379,41)
I do not understand what is wrong with my Join table mapping, it is one to many relationships and i expect multiple entries.
why does spring jpa adds unique key constraint to the join table.
Is there any way to make composite key constraint in Join table ? or how can i remove the constraint ?
Any advice will be highly appreciated.
Thanks.

Foreign key constraint #ManyToMany relationship preventing deletion

I've three associated records (Conference, SubmissionRecord, SubmissionAuthorRecord). Every SubmissionRecord has a Conference object and has a List<SubmissionAuthorRecord>.
When I delete a Conference record if the SubmissionRecord is associated with that Conference, it should cascade and delete as well. However, I keep getting a java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`viz`.`submission_record_author_set`, CONSTRAINT `FKgnqq52l26bitkmojk1oiuaki1`
FOREIGN KEY (`submission_record_s_id`) REFERENCES `submission_record` (`s_id`)) error message.
The table submission_record_author_set is create automatically and I have no entity that maps to it.
I understand the issue lies in the fact that the submission_record_author_set rows are preventing the SubmissionRecord from being deleted and have tried the #PreRemove method described here (How to remove entity with ManyToMany relationship in JPA (and corresponding join table rows)?) but to no avail. Maybe there's an issue with the ManyToMany annotation? Cause I do not see the equivalent annotation in the SubmissionAuthorRecord either.
#Entity
public class SubmissionRecord {
#Id
#GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "xyz")
#GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
#JsonSerialize(using = ToStringSerializer.class)
#Column(name = "s_id")
private Long id;
#Exportable(name = "Submission Id", nameInDB = "s_submission_id")
#Column(name = "s_submission_id")
private String submissionId;
// internal set of authors of the associated
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonIgnore
private List<SubmissionAuthorRecord> authorSet;
#JoinColumn(name="conference_id")
#ManyToOne(fetch = FetchType.EAGER)
#OnDelete(action = OnDeleteAction.CASCADE)
private Conference conference;
//...
}
#Entity
public class Conference {
#Id
#GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "xyz")
#GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
#JsonSerialize(using = ToStringSerializer.class)
private Long id;
private String creatorIdentifier;
private String conferenceName;
private String conferenceYear;
}
#Entity
public class SubmissionAuthorRecord {
#Id
#GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "xyz")
#GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
#JsonSerialize(using = ToStringSerializer.class)
#Column(name = "s_author_id")
private Long id;
private String dataSet;
#Column(name = "s_author_name")
private String name;
}
The submission_author_record_set table looks like the following:

Jpa OneToOne shared primary key half works

I have SpringBoot 2.1.3 and Java 8 application. Building DB with JPA I have 3 table in one to one relationship. Suppose the tables is the follows:
#Entity
#Data //lombok
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private Address address;
}
And then:
#Entity
#Data
#Table(name = "address")
public class Address {
#Id
#Column(name = "id")
private Long id;
#OneToOne
#MapsId
private User user;
}
That's works.. and it is the best way to do (this exactly example is taken from documentation).
If I start the application the DB is created and if I tried to add entities all works well. The model created follows:
Now I want to add a Country object to my address Entities (for example) and I modified the Entities as follows:
#Entity
#Data
#Table(name = "address")
public class Address {
#Id
#Column(name = "id")
private Long id;
#OneToOne
#MapsId
private User user;
#OneToOne
#MapsId
private Country country;
}
And Country Entities:
#Entity
#Data
#Table(name = "country")
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#OneToOne(mappedBy = "country", cascade = CascadeType.ALL)
private Address address;
}
The application still starts, the DB is created and the model follows:
But if I try to save a User as follows:
User user = new User();
Address address = new Address();
Country country = new Country();
user.setAddress(address);
address.setUser(user);
address.setCountry(country);
country.setAddress(address);
userRepository.save(user);
I obtain the error:
java.sql.SQLException: Field 'country_id' doesn't have a default value
Anyway I solve the issue removing #MapsId and added #JoinColumn but I would like to understand what's wrong.
P.S.: I'm using MySQL 5.7 with InnoDB dialect (setting on application.properties)
Thanks all
It works only with one #MapsId annotation. Using two is causing that country id is not inserted:
insert into Country (id) values (?)
insert into Users (id) values (?)
insert into Address (user_id) values (?)

Jpa ManytoMany issue with Spring Boot

I have a postgres database and I am trying to make a simple REST service with Spring Boot. I have a problem with jpa ManytoMany relationship.
Person Entity:
#Entity
#Table(name = "person", schema = "persons")
public class Person implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String email;
#Column
private Integer age;
#ManyToOne
#JoinColumn(name = "country_id", referencedColumnName = "id")
private Country countryOfBirth;
#ManyToMany
#JoinTable(
name="persons_countries_residence",
joinColumns=#JoinColumn(name="person_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="country_id", referencedColumnName="id"))
private List<Country> countriesOfResidence;
// getters and setters and to String method overriden
}
Country Entity:
#Entity
#Table(name = "country", schema = "persons")
public class Country implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(name = "country_name")
private String name;
#Column(name = "country_code")
private String code;
// getters and setters and to String method overriden
}
The postgres schema is the following:
Person Table:
CREATE TABLE persons.person
(
id serial NOT NULL,
name character varying(50) NOT NULL,
email character varying(40) NOT NULL,
age integer,
country_id serial NOT NULL,
CONSTRAINT id PRIMARY KEY (id),
CONSTRAINT country_id FOREIGN KEY (id)
REFERENCES persons.country (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Country table:
CREATE TABLE persons.country
(
id serial NOT NULL,
country_name character varying(45) NOT NULL,
country_code character varying(10) NOT NULL,
CONSTRAINT country_id PRIMARY KEY (id)
)
Join table:
CREATE TABLE persons.persons_countries_residence
(
person_id integer NOT NULL,
country_id integer NOT NULL,
CONSTRAINT person_country_id PRIMARY KEY (person_id, country_id),
CONSTRAINT persons_countries_residence_country_id_fkey FOREIGN KEY (country_id)
REFERENCES persons.country (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE NO ACTION,
CONSTRAINT persons_countries_residence_person_id_fkey FOREIGN KEY (person_id)
REFERENCES persons.person (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
)
When i make an HTTP method call, I don't get the Countries of residence.
Service code:
#RequestMapping(method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public List<Person> getAllPersons() {
retutn jpaPersonRepository.findAll();
}
Any help appreciated.
Maybe, you need to specify a schema name in the join table name:
#JoinTable(
name="persons_countries_residence", schema="persons",
joinColumns=#JoinColumn(name="person_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="country_id", referencedColumnName="id"))
Update your Country class code like :
#Entity
#Table(name = "country", schema = "persons")
public class Country implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(name = "country_name")
private String name;
#Column(name = "country_code")
private String code;
#ManyToMany(mappedBy = "countriesOfResidence")
private List<Person> persons;
// getters and setters and to String method overriden
}
Although a ManyToMany relationship is always bi-directional on the
database, the object model can choose if it will be mapped in both
directions, and in which direction it will be mapped in. If you choose
to map the relationship in both directions, then one direction must be
defined as the owner and the other must use the mappedBy attribute to
define its mapping. This also avoids having to duplicate the JoinTable
information in both places.
Do you mean that the country list is null? #ManyToMany associations are lazily loaded by default, you need to enable eager-fetching for it to work straight away.
#ManyToMany(fetch = FetchType.EAGER)
The solution is this:
#ManyToMany
#JoinTable(
name="persons_countries_residence", schema = "persons",
joinColumns=#JoinColumn(name="person_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="country_id", referencedColumnName="id"))
private List<Country> countriesOfResidence;
The schema had to be specified at the #JoinTable annotation as well.

Resources