I just switched from XML mapping to annotations and had to realize that my serialization class does not work any more!
I hope you can help me to find out the reason :)
I have a School class that contains an Address
#Entity
#Table(name="schools")
public class School {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn(name="address_id", nullable=true)
private Address address;
...
}
the Address class:
#Entity
#Table(name="addresses")
public class Address {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name="street", nullable=false, updatable=true)
private String street; // with nr
...
}
I try to serialize like this:
sessionFactory = MyFactory.getSessionFactory();
session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
School mbs = new School("interesting school");
Address a = new Address("garden street 5","12345", "somewhere");
mbs.setAddress(a);
session.save(mbs);
tx.commit();
This worked before with XML, but now doesn't..
The first Hibernate query action visible on the console is:
Hibernate: insert into schools (address_id, layout_id, name) values (?, ?, ?)
Therefore an error occurs: ERROR: Column 'address_id' cannot be null
How can I fix this?
.
Those are the tables:
CREATE TABLE schools(
id INTEGER NOT NULL AUTO_INCREMENT,
name CHAR(50),
address_id INTEGER NOT NULL,
layout_id INTEGER NOT NULL,
CONSTRAINT fk_address FOREIGN KEY (address_id) REFERENCES addresses(id),
CONSTRAINT fk_layout FOREIGN KEY (layout_id) REFERENCES layout_headers(id),
PRIMARY KEY (id)
);
CREATE TABLE addresses(
id INTEGER NOT NULL AUTO_INCREMENT,
street CHAR(55),
zip CHAR(6),
city CHAR(60),
CONSTRAINT addr_pk PRIMARY KEY (id)
);
In my hibernte.cfg.xml I have the following:
<hibernate-configuration>
<session-factory>
...
<mapping class="creator.models.school.Address" />
<mapping class="creator.models.school.Report" />
<mapping class="creator.models.school.School" />
<mapping class="creator.models.school.SchoolClass" />
...
</session-factory>
</hibernate-configuration>
You should specify cascade property of #ManyToOne annotation on address field of School class to save address before school. For example, CascadeType.ALL
Related
The following relationship creates a foreign key mapping
#Entity
public class Department {
#Id
private String name;
//some more fields
}
#Entity
public class Employee {
#Id
private long id;
private String name;
private String designation;
#ManyToOne
#JoinColumn(name = "fk_department_id", foreignKey = #ForeignKey(name="fk_department"))
private Department department;
}
generates:
...CONSTRAINT fk_department FOREIGN KEY (fk_department_id) REFERENCES department (name)
Question: how can I trigger this constraint creation in hibernate without having to create the Department entity?
Eg just adding the foreign key #Id field without an explicit entity reference. But still trigger the fk on initial creation. The following is of course invalid:
#ManyToOne
#JoinColumn(name = "fk_department_id", foreignKey = #ForeignKey(name="fk_department"))
private String department;
You get the intention. Is that possible?
(sidenote: I'm not interested in creating that foreign key link by startup ddl/sql statements).
You'll have to drop #ManyToOne at least, since that's for entities.
The following should work by overriding the column definition to include the foreign key while creating it
#Column(name = "department_id", columnDefinition = "VARCHAR(255), foreign key (department_id) REFERENCES department(name)")
private String department;
Now there's only a column and a constraint defined, but no relation (as far as Hibernate knows) defined between entities.
Essentially copied from Hibernate and JPA: how to make a foreign key constraint on a String but that was darn hard to find, so I'm not just going to close this as a duplicate! ;)
This is in reference to this answer.
Entities-
// Many to One
#Entity
#Table
public class Address {
#Id
#GeneratedValue
#Column
private int addressIdentity;
#Column
private int houseNo;
#Column
private char streetNo;
#Column
private int pincode;
#Column
private String city;
#Column
private String state;
#Column
private String country;
#ManyToOne
#JoinTable(name="PersonAddress",
joinColumns=#JoinColumn(name="addressId", insertable = false, updatable = false),
inverseJoinColumns=#JoinColumn(name="personId", insertable = false, updatable = false)
)
private Person person;
// getters and setters
One to Many
#Entity
#Table
public class Person {
#Id
#GeneratedValue
#Column
private int personId;
#Column
private String name;
#Column
private String designation;
#OneToMany
#JoinTable(name = "PersonAddress",
joinColumns = #JoinColumn(name = "personId"),
inverseJoinColumns = #JoinColumn(name = "addressId"))
private Set<Address> addSet = new HashSet<Address>();
// getters and setters
Hibernate configuration file-
<hibernate-configuration>
<session-factory name="">
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.connection.password">hello</property>
<property name="hibernate.connection.url">jdbc:postgresql://localhost:5432/xyz</property>
<property name="hibernate.connection.username">postgres</property>
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">create</property>
<mapping class="ManyToOne_OneToManyMappingWithJoinTable.Person" />
<mapping class="ManyToOne_OneToManyMappingWithJoinTable.Address" />
</session-factory>
</hibernate-configuration>
the persistence logic-
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
Person person1 = new Person();
person1.setName("Shahnaz Parveen");
person1.setDesignation("HouseWife");
Address address1 = new Address();
address1.setHouseNo(18);
address1.setStreetNo('E');
address1.setPincode(250002);
address1.setCity("Meerut");
address1.setState("UP");
address1.setCountry("INDIA");
address1.setPerson(person1);
Address address2 = new Address();
address2.setHouseNo(84);
address2.setStreetNo('1');
address2.setPincode(250002);
address2.setCity("Meerut");
address2.setState("UP");
address2.setCountry("INDIA");
address1.setPerson(person1);
person1.getAddSet().add(address1);
person1.getAddSet().add(address2);
session.save(address1);
session.save(address2);
session.save(person1);
session.getTransaction().commit();
session.close();
I am getting -
Jan 07, 2017 9:47:35 PM org.hibernate.action.internal.UnresolvedEntityInsertActions logCannotResolveNonNullableTransientDependencies
WARN: HHH000437: Attempting to save one or more entities that have a non-nullable association with an unsaved transient entity. The unsaved transient entity must be saved in an operation prior to saving these dependent entities.
Unsaved transient entity: ([ManyToOne_OneToManyMappingWithJoinTable.Person#0])
Dependent entities: ([[ManyToOne_OneToManyMappingWithJoinTable.Address#1]])
Non-nullable association(s): ([ManyToOne_OneToManyMappingWithJoinTable.Address.person])
Exception in thread "main" org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved beforeQuery current operation : ManyToOne_OneToManyMappingWithJoinTable.Address.person -> ManyToOne_OneToManyMappingWithJoinTable.Person
at org.hibernate.action.internal.UnresolvedEntityInsertActions.checkNoUnresolvedActionsAfterOperation(UnresolvedEntityInsertActions.java:122)
at org.hibernate.engine.spi.ActionQueue.checkNoUnresolvedActionsAfterOperation(ActionQueue.java:418)
at org.hibernate.internal.SessionImpl.checkNoUnresolvedActionsAfterOperation(SessionImpl.java:621)
at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:684)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:674)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:669)
at ManyToOne_OneToManyMappingWithJoinTable.ManyToOne_OneToManyMappingWithJoinTableImpl.main(ManyToOne_OneToManyMappingWithJoinTableImpl.java:40)
It works perfect with hbms.
Please suggest.
Thanks Vlad and Neil, it works but there is a problem described below-
This is the structure which gets created with HBMs. Hence the same must be with Annotations.
CREATE TABLE person_address
(
addressid integer NOT NULL,
personid integer NOT NULL,
CONSTRAINT person_address_pkey PRIMARY KEY (addressid , personid ),
CONSTRAINT fkkpp6mysmnyiywx3q33yxr1gbe FOREIGN KEY (personid )
REFERENCES person (person_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fkrpk0jx2y558su288tx9kd5cs6 FOREIGN KEY (addressid )
REFERENCES address (address_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
the moment I do -
#OneToMany(cascade = CascadeType.ALL, mappedBy = "person")
private Set<Address> addSet = new HashSet<Address>();
the join table structure is-
CREATE TABLE personaddress
(
personid integer,
addressid integer NOT NULL,
CONSTRAINT personaddress_pkey PRIMARY KEY (addressid),
CONSTRAINT fkfd5pm843bldj10y5kxwo37xge FOREIGN KEY (addressid)
REFERENCES address (addressidentity) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fkjuwlthwsi53bpf902nnl6snxh FOREIGN KEY (personid)
REFERENCES person (personid) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
You see that the primary key is NOT a combination of addressid and personid as in HBMs. Please suggest.
You need to add a cascade on the one-to-many side:
#OneToMany(cascade = CascadeType.ALL)
Then, change the #ManyToOne side to:
#ManyToOne
#JoinTable(name="PersonAddress",
joinColumns=#JoinColumn(name="addressId"),
inverseJoinColumns=#JoinColumn(name="personId")
)
private Person person;
and the #OneToMany side to:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "person")
private Set<Address> addSet = new HashSet<Address>();
To address the composite-key requirement as indicated by the question update, try mapping the join table (e.g. personaddress) as an entity, and use composite identifiers.
I want to perform CRUD operation over Postgres 9 using Hibernate.
Entity:
#Entity
#Table(name = "MESSAGE_HISTORY_RECORD")
public class MessageHistoryRecord {
#EmbeddedId
private MessageCompoundKey compoundKey;
#Column
private String responseChannel;
#ElementCollection
private List<Trace> traces;
#Column
private byte[] payload;
//getters and setters
}
Composite Id entity:
#Embeddable
public class MessageCompoundKey implements Serializable {
private static final long serialVersionUID = 9084329307727034214L;
#Column
private String correlatedMsgId;
#Column
private String messageId;
#Column
private String endpointId;
//getters and setters
}
ElementCollection Entity:
#Embeddable
public class Trace implements Serializable{
private static final long serialVersionUID = 9084329307727034214L;
private Long timestamp;
private String description;
//getters and setters
}
I am using hibernate.hbm2ddl.auto=update to create schema for me.
It created tables for me:
CREATE TABLE "public"."message_history_record"
(
correlatedmsgid varchar(255) NOT NULL,
endpointid varchar(255) NOT NULL,
messageid varchar(255) NOT NULL,
payload bytea,
responsechannel varchar(255),
CONSTRAINT message_history_record_pkey PRIMARY KEY (correlatedmsgid,endpointid,messageid)
)
;
CREATE UNIQUE INDEX message_history_record_pkey ON "public"."message_history_record"
(
correlatedmsgid,
endpointid,
messageid
)
;
CREATE TABLE "public"."messagehistoryrecord_traces"
(
messagehistoryrecord_correlatedmsgid varchar(255) NOT NULL,
messagehistoryrecord_endpointid varchar(255) NOT NULL,
messagehistoryrecord_messageid varchar(255) NOT NULL,
description varchar(255),
timestamp bigint
)
On persisting any object, I did not find any entry in messagehistoryrecord_traces table.
Hibernate properties:
hibernate.connection.driver_class=org.postgresql.Driver
hibernate.connection.url=jdbc:postgresql://192.xx.xx.xx:5432/testdb
hibernate.connection.username=***
hibernate.connection.password=****
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.connection.pool_size=10
hibernate.show_sql=true
hibernate.hbm2ddl.auto=update
Persist sql :
Hibernate: insert into MESSAGE_HISTORY_RECORD (payload, responseChannel, correlatedMsgId, endpointId, messageId) values (?, ?, ?, ?, ?)
According to your configuration the defaults should apply for the table name, column names and join column names of the collection table. These defaults are constructed as follows:
Table name: name of the referencing entity, appended with an underscore and the name of the entity attribute that contains the element colletion ( MessageHistoryRecord_traces)
Join column: name of the referencing entity, appended
with an underscore and the name of the primary key column of the entity table.
This second case is only allowed if you have a single primary key field in the parent entity which is not the case in your case. So you have specify the join column yourself as follows (I renamed the collection table name and foreign key column names because they are too long for my database system):
#ElementCollection
#CollectionTable(name = "mhr_traces",
joinColumns={#JoinColumn(name="mhr_correlatedmsgid", referencedColumnName="correlatedmsgid"),
#JoinColumn(name="mhr_endpointid", referencedColumnName="endpointid"),
#JoinColumn(name = "mhr_messageid", referencedColumnName = "messageid")})
private List<Trace> traces = new ArrayList<>();
And one more thing: you have to implement the equals() and hashCode() methods for the primary key class if you haven't done yet (they are not visible in your post).
Your table creation script is also missing the foreign key definitino (add them manually if they are not generated automatically):
CONSTRAINT mrFK FOREIGN KEY (mhr_correlatedmsgid, mhr_endpointid, mhr_messageid) REFERENCES MESSAGE_HISTORY_RECORD (correlatedmsgid,endpointid,messageid)
Adjust it matching to your database syntax (I don't know PostgreSQL)
With these adjustments everything works for me; indeed on an Oracle database system and EclipseLink as persistence provider. I think it is not implementation specific
Did you add anything to your traces list or it was empty??
It is working for me with postgresql without any tweaks. With hbm2ddl.auto set to update, hibernate created the tables and foreign key relationship between them as well. Here is the sample code I used :
public class App
{
public static void main( String[] args )
{
System.out.println("Maven + Hibernate + Postgresql");
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
MessageCompoundKey cKey = new MessageCompoundKey();
cKey.setCorrelatedMsgId("correlatedMsgId_2");
cKey.setEndpointId("endpointId_2");
cKey.setMessageId("messageId_2");
MessageHistoryRecord record = new MessageHistoryRecord();
record.setResponseChannel("ArsenalFanTv");
List<Trace> traces = new ArrayList<>();
Trace t1 = new Trace();
t1.setDescription("description_1");
t1.setTimestamp(System.currentTimeMillis());
traces.add(t1);
Trace t2 = new Trace();
t2.setDescription("description_2");
t2.setTimestamp(System.currentTimeMillis());
traces.add(t2);
record.setCompoundKey(cKey);
record.setTraces(traces);
session.save(record);
session.getTransaction().commit();
}
}
and my configuration file (hibernate.cfg.xml) is as follows :
<hibernate-configuration>
<session-factory>
<!-- <property name="hibernate.bytecode.use_reflection_optimizer">false</property> -->
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.connection.password">****</property>
<property name="hibernate.connection.url">jdbc:postgresql://127.0.0.1:5432/testdb</property>
<property name="hibernate.connection.username">****</property>
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<mapping class="com.skm.schema.MessageHistoryRecord"></mapping>
</session-factory>
</hibernate-configuration>
I was using StatelessSession instead of session. As per the documentation:
A stateless session does not implement a first-level cache nor interact with any second-level cache, nor does it implement transactional write-behind or automatic dirty checking, nor do operations cascade to associated instances. Collections are ignored by a stateless session. Operations performed via a stateless session bypass Hibernate's event model and interceptors. Stateless sessions are vulnerable to data aliasing effects, due to the lack of a first-level cache.
More details can be found on this thread.
After using Session instead of StatelessSession, it worked.
I have two entities having parent child relationship - Person and Address.
They have a one-to-one relationship.
I am able to cascade save both of them by one save.
But the below code does not add a foreign_key address_id to Person table.
SQL:
create table PERSON (
id number(20) NOT NULL ,
PRIMARY KEY (id)
);
alter table Person add address_id number(20);
alter table Person add CONSTRAINT FK_Address FOREIGN KEY (address_id) REFERENCES address (id);
create table Address (
id number(20) NOT NULL ,
PRIMARY KEY (id)
);
Hibernate code:
#Entity
#Table(name="Person")
#javax.persistence.SequenceGenerator(name="SOME_SEQ", sequenceName="pk_sequence")
public class Person
{
#Id #GeneratedValue(strategy = GenerationType.SEQUENCE, generator="SOME_SEQ")
private int id;
#OneToOne
#PrimaryKeyJoinColumn
#Fetch(FetchMode.JOIN)
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
private Address address;
}
#Entity
#javax.persistence.SequenceGenerator(name="SOME_SEQ", sequenceName="pk_sequence")
public class Address
{
#Id #GeneratedValue(strategy = GenerationType.SEQUENCE, generator="SOME_SEQ")
private int id;
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
#OneToOne(mappedBy="address")
#Fetch(FetchMode.JOIN)
private Person person;
}
Driving code:
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
Person p = new Person();
Address add = new Address ();
p.setAddress(add);
add.setPerson(p);
id = (Integer) session.save(p);
tx.commit();
Your mapping is wrong. You have a column in Person that is a foreign key to the address table. But you map the association with #PrimaryKeyJoinColumn, which means: The ID of this entity is also a foreign key to the address table. Use #JoinColumn. Not #PrimaryKeyJoinColumn.
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.