Invalid insert order when use #Inheritance JPA attribute - java

I have Eclipselink persistence provider tuned on DB2 DB. Where is 3 tables which simplified definition are listed below:
CREATE TABLE root
(
id CHAR(32) NOT NULL PRIMARY KEY,
rec_type VARCHAR(20)
);
CREATE TABLE derived
(
id CHAR(32) NOT NULL PRIMARY KEY,
...
);
ALTER TABLE derived ADD CONSTRAINT fk_derived_to_root FOREIGN KEY (id) REFERENCES root(id);
CREATE TABLE secondary
(
derived_id NOT NULL PRIMARY KEY,
...
);
ALTER TABLE secondary ADD CONSTRAINT fk_secondary_to_derived FOREIGN KEY (derived_id) REFERENCES derived(id);
Java entity classes for these entities are listed below,
RootEntity:
#javax.persistence.Table(name = "ROOT")
#Entity
#DiscriminatorColumn(name = "REC_TYPE")
#Inheritance(strategy = InheritanceType.JOINED)
public class RootEntity {
private String id;
#javax.persistence.Column(name = "ID")
#Id
#GeneratedValue(generator = "system-uuid")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
private String principalType;
#Column(name = "PRINCIPAL_TYPE")
public String getPrincipalType() {
return principalType;
}
public void setPrincipalType(String principalType) {
this.principalType = principalType;
}
...
}
DerivedEntity:
#javax.persistence.Table(name = "DERIVED")
#Entity
#DescriminatorValue("DERIVED")
public class DerivedEntity extends RootEntity {
private SecondaryEntity secondaryEntity;
#OneToOne(mappedBy = "derived_id")
public SecondaryEntity getSecondaryEntity() {
return secondaryEntity;
}
public void setSecondaryEntity(SecondaryEntity secondaryEntity) {
this.secondaryEntity = secondaryEntity;
}
...
}
I see no derived table insertion in the test logs:
--INSERT INTO ROOT (ID, REC_TYPE) VALUES (?, ?)
bind => [241153d01c204ed79109ce658c066f4c, Derived]
--INSERT INTO SECONDARY (DERIVED_ID, ...) VALUES (?, ...)
bind => [241153d01c204ed79109ce658c066f4c, ...]
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.ibm.db2.jcc.am.fo: DB2 SQL Error: SQLCODE=-530, SQLSTATE=23503, SQLERRMC=SCHEM.SECONDARY.FK_SECONDARY_TO_DERIVED, DRIVER=3.57.82
So question is: why Eclipselink don't insert new record into DERIVED table prior to insertion to SECONDARY table?
P.S. Everything is working fine when no SECONDARY table (ROOT and DERIVED tables only) or no inheritance used (DERIVED tables generates id).

For inheritance JPA assumes the foreign key constraints in related table refer to the root table.
You can change your constraint to refer to the root table, or,
use a DescriptorCustomizer to set,
descriptor.setHasMultipleTableConstraintDependecy(true);
or,
customizer the OneToOneMapping to have its foreign key refer to the secondary table (JPA annotation always make it refer to the root table).
Please log a bug though, as JPA join columns should allow you to define a foreign key to the secondary table.
The reason that EclipseLink does defer the insert into the secondary table is to allow inserts to be grouped by tables to allow batch writing and avoid database deadlocks.

Related

Why Can't I Delete this Record (Table Constraints) - I am using Spring and Hibernate

I am having problem deleting a record from my database table. I get Constraintviolation exception. I'm dealing with Categories and Sub-categories. I have a Category class that has parent which also refer to the same Category class. Categories with no parent will have the parent field be null. In the table below all empty parent columns means those categories are parent categories.
I am using Spring boot and Spring data so the repository and the delete() methods are created automatically. I just call the delete() method in my controller class.
I have the following class
#Entity
#Table(name="category")
public class Category {
#Id
#Column(name="id")
private String id = IdGenerator.createId();
#Column(name="name", nullable=false, unique=true)
#Size(min=2, max=60, message="LATER")
private String name;
#ManyToOne//(cascade=CascadeType.ALL)
#JoinColumn(name="parent", nullable=true)
private Category parent;
#OneToMany(mappedBy="parent")
private Set<Category> children = new HashSet<Category>();
//GETTERS AND SETTERS HERE
}
and the following table ::
CREATE TABLE category(
id varchar(255) NOT NULL,
name varchar(255) NOT NULL,
parent varchar(255) DEFAULT NULL,
PRIMARY KEY (id),
FOREIGN KEY (parent) REFERENCES category (id)
);
with the following records in the table::
id name parent
1 Furnitre
2 barbecue 1
3 Home and Garden
4 Fashion 3
5 Restaurants 3
Now when i try to delete record 5 (Category record in row 5) i get
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`grabone`.`category`, CONSTRAINT `category_ibfk_1` FOREIGN KEY (`parent`) REFERENCES `category` (`id`))
I think this shouldn't be the case because record 5 has no children and i have to be able to delete it.
What am i doing wrong?
UPDATE
This is my controller code to delete Category. There is something rare going on here ::
#DeleteMapping(value = DELETE)
public ResponseEntity<?> delete(#PathVariable("categoryId") String categoryId){
Category cat = categoryService.findById(categoryId);
categoryService.delete(cat);
return new ResponseEntity<>("", HttpStatus.OK);
}
The above code works now. It deletes Category from the table but the following don't. It generates the above Exception. WHY?
#DeleteMapping(value = DELETE)
public ResponseEntity<?> delete(#PathVariable("categoryId") String categoryId){
categoryService.delete(categoryId);
return new ResponseEntity<>("", HttpStatus.OK);
}
In Spring Data repository, there are two delete() methods::
void delete(String id);
void delete(Category category);
One that takes id and the other that takes the Object of the class. Why is it that the one that takes the id doesn't work?

Joining two tables Hibernate

I am working with Spring MVC + Hibernate ( Spring data jpa ).
Here I am not getting how to create a table in Hibernate for the following scenario.
I have two tables :
1) User Details
2) Branch
Now, For every user, there is a field for branch, and that value should be from Branch table.
I have knowledge of OneToOne in hibernate. But it inserts a new entry for users branch field in Branch table. What I want is that, when I save user details in User Table, branch details should be just a reference from Branch table for matching row.
Thank you in advance
Suppose your branches can be identified by their names:
UserDetails user = new UserDetails();
...
user.setBranch(branchRepository.findOneByName());
...
userDetailsRepository.save(user);
Having:
#Entity
public class UserDetails {
#Id
#GeneratedValue
Long id;
#ManyToOne
Branch branch;
...
}
#Entity
public class Branch {
#Id
#GeneratedValue
Long id;
...
}
public interface BranchRepository extends Repository<Branch, Long> {
...
}
public interface UserDetailsRepository extends Repository<UserDetails, Long> {
...
}
You can use the User-Branch relationship by controlling the association through its Foreign Key.
And inside the User class, you will specify the OneToOne mapping as follows:
#OneToOne
#JoinColumn(name="User_Branch_ID")
private Branch branch;
And this "User_Branch_ID" refers to the foreign key which you have created while creating the User database table as follows:
create table BRANCH (
branch_id BIGINT NOT NULL AUTO_INCREMENT,
   name VARCHAR(30) NOT NULL,
   city  VARCHAR(30) NOT NULL,
   country  VARCHAR(30) NOT NULL,
   PRIMARY KEY (address_id)
);
 
create table USER (
   user_id BIGINT NOT NULL AUTO_INCREMENT,
   user_branch_id BIGINT NOT NULL,
   first_name VARCHAR(30) NOT NULL,
   last_name  VARCHAR(30) NOT NULL,
   PRIMARY KEY (user_id),
   CONSTRAINT user_branch FOREIGN KEY (user_branch_id) REFERENCES BRANCH ( branch_id)
);
step 1: create a view
create view v_BRANCH_USER as
select
a.branch_id, a.name , a.city, a.country
b.user_id,b.first_name, b.last_name
from BRANCH a, USER b
where a.branch_id = b.user_branch_id
step 2: create a pojo and mapping to hibernate as a table
#Entity
#Table(name = "v_BRANCH_USER")
public class VBranchUser
String userId;
....
}
step 3: You can query it as a table (Criteria, HQL ..)

Spring JPA insert entity with id equals null (should be auto generated)

Spring's JpaRepository throws an exception when I try to save entity without id:
MyConfig config = new MyConfig();
config.setValue("value");
myConfigRepository.save(config);
How to make Hibernate not include id field into insert query?
org.h2.jdbc.JdbcSQLException: NULL not allowed for column "MY_CONFIG_ID"; SQL statement:
insert into my_config (value, id) values (?, ?) [23502-190]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:179)
at org.h2.message.DbException.get(DbException.java:155)
at org.h2.table.Column.validateConvertUpdateSequence(Column.java:305)
at org.h2.table.Table.validateConvertUpdateSequence(Table.java:749)
at org.h2.command.dml.Insert.insertRows(Insert.java:151)
at org.h2.command.dml.Insert.update(Insert.java:114)
at org.h2.command.CommandContainer.update(CommandContainer.java:78)
at org.h2.command.Command.executeUpdate(Command.java:253)
at org.h2.server.TcpServerThread.process(TcpServerThread.java:345)
at org.h2.server.TcpServerThread.run(TcpServerThread.java:159)
at java.lang.Thread.run(Thread.java:745)
my entity:
#Entity
public class MyConfig {
#Id
#SequenceGenerator(sequenceName = "MY_CONFIG_SEQ", name = "MyConfSeq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MyConfSeq")
private Long id;
#Column
private String value;
//getters & setters
and repository:
public interface MyConfigRepository extends JpaRepository<MyConfig, Long>, JpaSpecificationExecutor {}
The column MY_CONFIG_ID is not part of your Hibernate mapping.
The query for insert inserts into the fields id and value. Then, there must be a third column named MY_CONFIG_ID, which is not mentioned in the entity, and thus will be inserted as null. If this column then has a not null constraint, it will fail.
If you want your id-column to be named MY_CONFIG_ID then you need to ovveride the default column name (id, same as the variable) by annotating it with #Column(name="MY_CONFIG_ID"), if you want to use the default name id you would need to remove the column (or at least the not null constraint.)

JPA 2: Referential integrity constraint violation while saving entity with undirectional OneToMany relationship

I'm working on database design and Java implementation of some sports event model.
I have two entities: Game and Participant and undirectional OneToMany relationship between them. Game can has many Participant's.
In database it looks like:
Of course i have FK constraint on table event_participant:
CONSTRAINT `FK_par_eve_eve_id__eve_id`
FOREIGN KEY (`event_id`)
REFERENCES `event` (`id`));
In Java code i wrote this mapping (simplified):
#Entity
#Table(name = "event")
public class GameEntity extends AbstractPersistable<Long> implements Game {
#Nullable
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "game")
private List<ParticipantEntity> participants;
}
#Entity
#Table(name = "event_participant")
public class ParticipantEntity extends AbstractPersistable<Long> implements GameParticipant {
#ManyToOne
#JoinColumn(name="event_id", referencedColumnName = "id", insertable = true)
private GameEntity game;
}
Test:
TeamEntity teamHome = teamsDao.findOne(1L);
TeamEntity teamGuest = teamsDao.findOne(2L);
GameEntity newEntity = new GameEntity()
.addParticipant(teamHome, GameParticipant.Alignment.HOME)
.addParticipant(teamGuest, GameParticipant.Alignment.GUEST);
dao.save(newEntity);
public GameEntity addParticipant(TeamEntity team, GameParticipant.Alignment alignment) {
if (participants == null) {
participants = new ArrayList<ParticipantEntity>();
}
participants.add(ParticipantEntity.create(team, alignment));
return this;
}
public static ParticipantEntity create(TeamEntity team, Alignment alignment) {
ParticipantEntity object = new ParticipantEntity();
object.team = team;
object.alignment = alignment;
return object;
}
When i'm trying to save the new GameEntity object which contains just created Participant's objects i expect that Hibernate creates one new record in event table and related records in event_participant table.
But i got an exception:
Referential integrity constraint violation: "FK_PAR_EVE_EVE_ID__EVE_ID: PUBLIC.EVENT_PARTICIPANT FOREIGN KEY(EVENT_ID) REFERENCES PUBLIC.EVENT(ID) (0)"; SQL statement:
insert into event_participant (id, alignment, event_id, participant_id, participant_type) values (null, ?, ?, ?, ?)
This insert query contains NULL value for event_id field. I don't understand why.
When i removed DB constraint everything is ok and event_id column has the correct value.
All the boilerplate is done by Spring Data util classes.
I'm using JPA 2.0, mysql-connector-java 5.1.28, hibernate-entitymanager 4.0.1.Final, spring-data-jpa 1.3.4.RELEASE and H2 database for testing.
I got the same result using either H2 or MySQL.
Where i failed?
You didn't set the other side of this bi-directional association:
public GameEntity addParticipant(TeamEntity team, GameParticipant.Alignment alignment) {
ParticipantEntity pe = ParticipantEntity.create(team, alignment)
participants.add(pe);
pe.setGame(this);
return this;
}
The thing to note is you didn't set the foreign key field for the participant as in pe.setGame(this)

Inheritance in hibernate

I would like to implement inheritance in Hibernate.
I created ObjectClass object:
#Entity
#Table(name = "object")
#Inheritance(strategy = InheritanceType.JOINED)
public class ObjectClass {
private id;
}
and CodeTable object that inhertance Object class:
#Entity
#ForeignKey(name = "id")
#Table(name = "code_table")
public class CodeTable extends ObjectClass{
private String description;
}
in the database
object table is:
CREATE TABLE `object` (
`id` bigint(11) NOT NULL auto_increment,
PRIMARY KEY (`id`),
)
code_table table is:
-
CREATE TABLE `code_table` (
`id` bigint(11) NOT NULL auto_increment,
`description` varchar(45) character set latin1 default NULL,
PRIMARY KEY (`id`),
KEY `FK_object` (`id`),
CONSTRAINT `FK_object` FOREIGN KEY (`id`) REFERENCES `object` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
)
I wrote the following code to retreive data from codeTable:
#SuppressWarnings( "unchecked" )
#Transactional( readOnly = true, propagation = Propagation.REQUIRED )
public Collection<CodeTable> findAll() {
Session session = getSessionFactory().getCurrentSession();
return
session.createCriteria( persistentClass
).setResultTransformer( Criteria.DISTINCT_ROOT_ENTITY
).list();
}
I gets empty list although there is one record in codetable table.
When I write the following SQL in my database:
SELECT * FROM `code_table`
I get:
id= 1,
description = company.
What went wrong in my Hibernate definition? How can I retrieve the object?
EDITED:
My hibernate.cfg.xml file looks like this:
<hibernate-configuration>
<session-factory>
<mapping class="com.mycompany.model.CodeTable" />
<mapping class="com.mycompany.model.ObjectClass" />
</session-factory>
</hibernate-configuration>
Your mappings and table structure are (roughly) correct for a JOINED inheritance strategy and I cannot reproduce your problem.
I use the following mappings (which are basically the one you provided):
#Entity
#Table(name = "object")
#Inheritance(strategy = InheritanceType.JOINED)
public class ObjectClass {
#Id #GeneratedValue
private Long id;
public ObjectClass() { }
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
}
And
#Entity
#ForeignKey(name = "id")
#Table(name = "code_table")
public class CodeTable extends ObjectClass{
private String description;
public CodeTable() { }
public String getDescription() { return description; }
public void setDescription(String description) {
this.description = description;
}
#Override
public String toString() {
return "CodeTable [getDescription()=" + getDescription() + ", getId()="
+ getId() + "]";
}
}
The following tables:
create table code_table (
description varchar(255),
id bigint not null,
primary key (id)
)
create table object (
id bigint not null,
primary key (id)
)
alter table code_table
add constraint id
foreign key (id)
references object
And the following parent/child records:
insert into object values (1);
insert into code_table(id, description) values (1, 'foo');
And running your criteria query:
session.createCriteria(CodeTable.class)
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.list();
Returns:
CodeTable [getDescription()=foo, getId()=1]
Everything works as expected.
References
JPA 1.0 Specification
2.1.10 Inheritance Mapping Strategies
Hibernate Annotations Reference Guide
2.2.4. Mapping inheritance
How does your mapping looks like ?
Have you read this section in the Hibernate doc ?
Inheritance mapping in Hibernate
As you can read in the link I provided above, your mapping is not correct. You have to let Hibernate know that the code_table class inherits from the object class, and you 'll have to let Hibernate know how this link exists in the database.

Categories

Resources