My classes look simiar to this: (Offer class)
#Entity
public class Offer {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
#ElementCollection(fetch = FetchType.LAZY)
private Set<Product> products;
...other fields
}
and Product class:
#Embeddable
public class Product {
private String name;
private String description;
private int amount;
}
The problem is when I try to persist the Offer entity and try to add two objects to Offer's Set:
Product product = new Product("ham", "description1", 1);
Product product = new Product("cheese", "description2", 1);
I receive exception:
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "offer_products_pkey"
Details: Key (offer_id, amount)=(1, 1) already exists.
I have no idea why can't I persist two embeddable objects in the Set when one of them has the same value of "amount" field? Is it treated as ID somehow?
Maybe I should not create list of embeddable objects as it is not designed to be used like this? If so - then what if I dont need an entity of Product but want to keep set of it in another entity (Offer)?
Thanks in advance for help
The issue when using a Set is that the contents must be unique, since that is the definition of a Set. The JPA provider will try to enforce this with a database constraint. In your example it adds a constraint in the form of a Primary Key the Offer_id and the int Amount, though IMHO it should be adding a constraint for all the values of Product property. The best way to see this is to enable the SQL logs and see what is going on under the covers:
Hibernate: create table Offer (id integer not null, primary key (id))
Hibernate: create table Offer_products (Offer_id integer not null, amount integer not null, description varchar(255), name varchar(255), primary key (Offer_id, amount))
Hibernate: alter table Offer_products add constraint FKmveai2l6gf4n38tuhcddby3tv foreign key (Offer_id) references Offer
The way to fix this is to make the products property of Offer a List instead of the Set:
Hibernate: create table Offer (id integer not null, primary key (id))
Hibernate: create table Offer_products (Offer_id integer not null, amount integer not null, description varchar(255), name varchar(255))
Hibernate: alter table Offer_products add constraint FKmveai2l6gf4n38tuhcddby3tv foreign key (Offer_id) references Offer
Related
Helllo, I've been trying to create an Entity named ContratoAluguer, however each time I try to create this Entity the following error occurs:
Internal Exception: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "ID"; SQL statement:
INSERT INTO CONTRATOALUGUER (CLIENT_ID, AUTOMOVEL_ID) VALUES (?, ?) [23502-200]
Error Code: 23502
Call: INSERT INTO CONTRATOALUGUER (CLIENT_ID, AUTOMOVEL_ID) VALUES (?, ?)
bind => [2 parameters bound]
-----------------------Entity Code Below-----------
package isep.eapli.demo_orm.domain;
import javax.persistence.*;
import java.util.List;
#Entity
public class ContratoAluguer {
#OneToOne
private Automovel automovel;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "ContratoId")
private List<CondutorAutorizado> condutorAutorizadoList;
#ManyToOne(cascade = CascadeType.ALL)
private Cliente client;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public ContratoAluguer(Automovel automovel, List<CondutorAutorizado> condutorAutorizadoList, Cliente client) {
this.automovel = automovel;
this.condutorAutorizadoList = condutorAutorizadoList;
this.client = client;
}
From error : you are trying to insert NULL value into PRIMARY KEY column, which obviously has not null constraint.
You are expecting DB to take care of generating ID for your entity - what i see from generation type strategy.
You are probably using H2 DB ? So check table definition.
Your primary key should be of type 'bigint' (http://www.h2database.com/html/datatypes.html#bigint_type).
About identity in H2, documentation says:
Identity column is a column generated with a sequence. The column declared as the identity column with IDENTITY data type or with IDENTITY () clause is implicitly the primary key column of this table. GENERATED ALWAYS AS IDENTITY, GENERATED BY DEFAULT AS IDENTITY, and AUTO_INCREMENT clauses do not create the primary key constraint automatically. GENERATED ALWAYS AS IDENTITY clause indicates that column can only be generated by the sequence, its value cannot be set explicitly. Identity column has implicit NOT NULL constraint. Identity column may not have DEFAULT or ON UPDATE expressions.
So your SQL should looks something like this :
CREATE TABLE TEST(
ID BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
...);
I ma guessing you are using JPA (from the annotations). So if you are letting JPA to generate table for you, check table in H2 console as i mentioned earlier.
This would be my first step from information you provided.
Hope this helps
I have two tables:
language
CREATE TABLE language (
id BIGSERIAL,
name TEXT NOT NULL UNIQUE,
PRIMARY KEY (id)
);
translation
CREATE TABLE translation (
id BIGSERIAL,
language_id BIGINT REFERENCES language (id),
translation_key TEXT NOT NULL,
translation_value TEXT NOT NULL,
PRIMARY KEY (id)
);
And I would like to get such entity, where translation table (primary table) joins language table by language_id from primary table. Problem: at the moment it joins by translation PK(id).
#Entity
#Table(name = "translation")
#SecondaryTable(name = "language", pkJoinColumns = #PrimaryKeyJoinColumn(name = "id"))
public class Translation {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(table = "language", name = "name")
// ON translation.language_id = language.id
private String language;
#Column(name = "translation_key")
private String translationKey;
#Column(name = "translation_value")
private String translationValue;
// getters and setters
}
Where I should specify it in my code to do it correctly?
SQL example: SELECT t.id, l.name, translation_key, translation_value FROM translation t INNER JOIN language l on t.language_id = l.id;
You cannot use #SecondaryTable for the purpose you describe.
#SecondaryTable is used when a single entity is spread across multiple tables. Each of these 'pieces' of the entity must be privately owned by the entity, and is in a one-to-one relation with every other 'piece'.
If you want a many-to-one relation between translations and languages, you need to use #ManyToOne (and create a separate Language entity) instead.
#SecondaryTable(name = "language")
this way it is going to generate value for the translation id and insert it to the language foreign key automatically if you specify pkJoinColumn it is going to relate the tables through the primary key while if you don't mention that, it would do it through the foreign key. After that you need to create a trigger and sequence for the language table id column. It should work.
I have two tables:
part {
int id; --> primary key, auto generated
varchar poNo;
varchar partNo;
varchar partDesc;
varchar eccNo;
...
}
supplement {
int id; --> primary key
varchar poNo; --> foreign key
varchar partNo; --> foreign key
varchar venderPartNo;
varchar exportHSCCode;
...
}
I defined one Entity as below:
#Entity
#Table(name="part")
#SecondaryTable(name="supplement", pkJoinColumns ={#PrimaryKeyJoinColumn="id"})
public class Part{
#Id
#GeneratedValue
private Integer id;
private String poNo;
private String partNo;
private String partDesc;
private String eccNo;
#Column(table="supplement")
private String vernderPartNo;
#Column(table="supplement")
private String exportHSCCode;
...
getter and setter...
}
Question 1:
when I persist one part, I dob't want to insert one row into supplement table, is there has any configuration or annotation can get it? Because according to above Entity and configuration , when I persist one part, hibernate will generate two insert SQL statement for me:
insert into part(poNo, partNo, partDesc, eccNo) values (?,?,?,?)
insert into supplement(vernderPartNo, exportHSCCode, id) vaules (?,?,?)
which I want is, when persist one part, I didn't set value for any filed of supplement, then just one insert statement:
insert into part(poNo, partNo, partDesc, eccNo) values (?,?,?,?)
is it possible?
Question 2:
from the table schema of above, the poNO and partNo is the foreign key, that means , for every related part and supplement, this two field should be has the same value. But I don't know how to map this two column value to supplement table when using the configuration as above.
As for the normal operation for Hibernate, when it process one part, it always generate two insert statement which I mentioned above, and for the secondary table, it's insert statement just contains those fields which has specified it's table name, so for supplement, it's insert statement is :
insert into supplement(vernderPartNo, exportHSCCode, id) vaules (?,?,?)
So, is there has any way to let Hibernate generate the insert statement as below:
insert into supplement(poNo, partNo, vernderPartNo, exportHSCCode, id) vaules (?,?,?,?,?)
to map two tables with #SecondaryTable
you can use like below on the primary table
#Table(name = "Table_Primary", schema = "schema ")
#SecondaryTable(name = "Table_Secondary", schema = "schema ", pkJoinColumns = {#PrimaryKeyJoinColumn(name = "Table_Primary_Column", referencedColumnName = "Table_Secondary_Column")})
1) Try the following (I would expect this to work, but Hibernate may not play this way):
#Entity
#Table(name="part")
#SecondaryTable(name="supplement", pkJoinColumns ={#PrimaryKeyJoinColumn="id"})
public class Part{
#Id
#GeneratedValue
private Integer id;
private String poNo;
private String partNo;
private String partDesc;
private String eccNo;
#Column(table="supplement")
#Basic(optional=true)
private String vernderPartNo;
#Column(table="supplement")
#Basic(optional=true)
private String exportHSCCode;
...
getter and setter...
}
2) You'll need to change how your keying Part. You've defined its primary key as an auto incremented integer...that's gonna be your foreign key in the supplement table. This is actually how JPA wants you to do it (I say that because the JPA specification calls natural keys "legacy"). You have a couple options here:
remove the autoincrement from the Part entity and create a "composite key" class with just the poNo and partNo, and add that class as a field to Part. This will become your Part primary key, and will be the foreign key used in your supplement table.
Forget foreign keys and instead add a #UniqueConstraint to your Part for those two columns (this isn't going to fix your foreign key issue, but it does enforce the constraint you identified)
I'm trying to persist a very simple Unidirectional One to Many relationship, but EclipseLink (2.3.1) fails.
Service Class (Parent):
#Entity
#Table(name = "tbl_service2")
public class Service implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="service_id")
public long serviceID;
#Column(name="name")
public String name;
#OneToMany(cascade={CascadeType.ALL})
#JoinColumn(name="service_id", referencedColumnName="service_id")
public Set<Parameter> parameters;
}
Parameter Class (Child):
(Of course there is "service_id" foreign key field in the database, which is not represented in the class, as it's unidirectional relation).
#Entity
#Table(name = "tbl_service_parameters2")
public class Parameter implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="param_id")
public long parameterID;
#Column(name="name")
public String name;
}
And this is the code for Entity persistence:
Service service = new Service();
service.parameters = new HashSet<Parameter>();
service.name = "test";
Parameter param = new Parameter();
param.name = "test";
service.parameters.add(param);
em.persist(service);
em.flush();
I get this exception:
Internal Exception: java.sql.SQLException: Field 'service_id' doesn't have a default value
Error Code: 1364
Call: INSERT INTO tbl_service_parameters2 (name) VALUES (?)
bind => [test]
EDIT: The database field service_id has (and should have) not-null constraint, due the nature of the data.
Is this a bug or is something wrong in the code?
Use nullable = false, on #JoinColumn:
#JoinColumn(name = "service_id", nullable = false)
Try removing the not null constraint on the Parameter table's service_id field. Eclipselink will update the foreign key for unidirectional 1:m join columns in a separate statement, so you'll need to disable or delay the constraint check. Making it bidirectional will allow the fp field to be updated with the rest of the parameter data.
You can change your persistence for hibernate version<4.0 and your code will run well."Well" in reference " for one-to-many relation save/persist parent ONLY, NOT save/persist child's collection by separate task"
I was able to get it to work in Oracle by using a deferrable foreign key.
Example:
ALTER TABLE my_table ADD CONSTRAINT my_constraint_name FOREIGN KEY (my_table_column) REFERENCES foreign_key_table (foreign_key_table_column) DEFERRABLE INITIALLY DEFERRED
By default nullable is true on #JoinColumn, while persisting the data in one to many relationship, we need to make nullable as false to avoid data violation exceptions that occurs at run-time.
As I found out, in such cases, foreign key is filled in a separate statement. In my example, I used Address entity with customer_id as foreign key.
2014-07-08T20:51:12.752+0300|FINE: INSERT INTO ADDRESS (address_id, street, city, region) VALUES (?, ?, ?, ?)
bind => [10, foo, foo, foo]
2014-07-08T20:51:12.753+0300|FINEST: Execute query InsertObjectQuery(ua.test.customer.Address#28cef39d)
2014-07-08T20:51:12.757+0300|FINEST: Execute query DataModifyQuery(sql="UPDATE ADDRESS SET customer_id = ? WHERE (address_id = ?)")
2014-07-08T20:51:12.757+0300|FINE: UPDATE ADDRESS SET customer_id = ? WHERE (address_id = ?)
bind => [151, 10]
Therefore, having #JoinColumn with nullable=true causes an error.
As alternative, you can use #OneToMany (..., orphanRemoval = true, ...).
I'm trying to map the objects to the following join table,
Table name: booking_pax
3 Columns: booking_id, pax_id, pax_no_in_tour
Primary Key: booking_id, pax_id
Foreign Key: booking_id ref booking table, pax_id ref pax table
CREATE TABLE `booking_pax` (
`booking_id` int(8) unsigned NOT NULL,
`pax_id` int(8) unsigned NOT NULL,
`pax_numb_in_tour` int(2) unsigned DEFAULT NULL,
PRIMARY KEY (`booking_id`,`pax_id`),
KEY `booking_booking_pax` (`booking_id`),
KEY `pax_booking_pax` (`pax_id`),
CONSTRAINT `booking_booking_pax` FOREIGN KEY (`booking_id`) REFERENCES `booking` (`booking_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `pax_booking_pax` FOREIGN KEY (`pax_id`) REFERENCES `pax` (`pax_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
)
I have two entities: pax and booking. Due to the relationship is ManyToMany, and there is one extra column need to add into this join table.
Now I have problem to mapping with Hibernate.
Modelling:
#Entity
public class Booking {
......
#ManyToMany
#JoinTable(
name="booking_pax",
joinColumns={#JoinColumn(name="booking_id")},
inverseJoinColumns={#JoinColumn(name="pax_id")}
)
//#ElementCollection
//#Column(name="pax_numb_in_tour")
public Map<Pax, Integer> getPaxs() {
return paxs;
}
......
}
The error msg:
org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class: com.....model.Booking.paxs[java.lang.Integer]
I have tried many ways to map the collection, but still not working.
The IDE I'm using is MyEclipse 8.6 with JaveEE 5. I tried to upgrade to JavaEE 6 but failed.
Thanks.
untested:
#ElementCollection
#CollectionTable(
name="booking_pax",
joinColumns={#JoinColumn(name="booking_id")}
)
#Column(name="pax_numb_in_tour")
#MapKeyJoinColumn(name="pax_id")
public Map<Pax, Integer> getPaxs() {
return paxs;
}