I want to create a Hibernate XML mapping for the following POJO Classes:
public class Topics {
private int topicId;
private Map<Integer,News> news;
//setters and getters
}
public class News {
private int newsId;
private Map<Integer,Topics> newstopics;
//setters and getters
}
My Tables are these:
CREATE TABLE topics (
topicid int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (topicid)
)
CREATE TABLE news (
id int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
)
CREATE TABLE newstopics (
topicid int(11) NOT NULL,
newsid int(11) NOT NULL,
PRIMARY KEY (topicid,newsid),
CONSTRAINT FOREIGN KEY (topicid) REFERENCES topics (topicid) ON DELETE CASCADE,
CONSTRAINT FOREIGN KEY (newsid) REFERENCES news (id) ON DELETE CASCADE
)
I am trying something like this, but it is not working:
<class name="Topics" table="topics">
<id name="topicId" type="int" column="topicid"><generator class="native"/></id>
<map name="news" table="newstopics">
<key column="topicid"/>
<map-key column="newsid" type="int"/>
<many-to-many column="newsId" class="News" />
</map>
</class>
<class name="News" table="news">
<id name="newsId" type="int" column="id"><generator class="native"/></id>
<map name="newstopics" table="newstopics">
<key column="newsid" />
<map-key column="topicid" type="int"/>
<many-to-many column="topicId" class="Topics" />
</map>
</class>
Goal is to achieve a bidirectional mapping, meaning that when something changes in one map the contents of newstopics get updated too. I have tried multiple variations for the mapping of the maps but no luck. The most disturbing aspect ist that the hibernate genarated SELECTs and INSERTs from/to the newstopics table are looking for more that its 2 columns. For example INSERT INTO newstopics (topicid,newsid,id) VALUES ? ? ?
Related
This is a simple description of my situation and the requirement I am not able to implement:
I have two tables "address" and "person" related with a FK:
CREATE TABLE `address` (
`id_address` int(11) NOT NULL,
`street` varchar(250) DEFAULT NULL,
`city` varchar(250) DEFAULT NULL,
PRIMARY KEY (`id_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `person` (
`id_person` int(11) NOT NULL,
`name` varchar(250) DEFAULT NULL,
`id_sent_address` INT(11) NOT NULL,
PRIMARY KEY (`id_person`),
CONSTRAINT `fkSentAddress` FOREIGN KEY (`id_sent_address`) REFERENCES `address` (`id_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And I want to create a hibernate mapping using this two tables to create a simple model.
It is important the fact that I NEED to do the mapping using "join" having something like this:
<class name="xxx.PersonAndAddress" table="person">
<id name="idPerson" type="java.lang.Integer">
<column name="id_person" />
<generator class="identity" />
</id>
<property name="name" type="java.lang.String" column="name"/>
<join table="address">
<key column="id_address" />
<property name="street" column="street" />
<property name="city" column="city" />
</join>
</class>
This is not working because the "join" key column "id_address" is creating the mapping against the primary key "id_person", and I don't know how can I change this behaviour column to use the person's "id_sent_address" instead "id_person". Is there any solution?
As I said, this is a simple situation that describes my real problem, which involves more fields and more logic, but this is the essence of the issue.
Right now I'm using formulas for each address's field but it is a big quantity of fields and I would use join to improve its performance.
Thanks a lot!
Two things, first of all, ORDER MATTERS. You need to create your address table first, before you can create your person table, as person table's id_sent_address DEPENDS on the id_address from address table.
Secondly, to set up a foreign key, the target key MUST be a key as well, which you forgot to add.
So once the two errors are fixed like below, you should be able to execute it without errors.
CREATE TABLE `address` (
`id_address` int(11) NOT NULL,
`street` varchar(250) DEFAULT NULL,
`city` varchar(250) DEFAULT NULL,
PRIMARY KEY(`id_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `person` (
`id_person` int(11) NOT NULL,
`name` varchar(250) DEFAULT NULL,
`id_sent_address` INT(11) NOT NULL,
PRIMARY KEY (`id_person`),
CONSTRAINT `fkSentAddress` FOREIGN KEY (`id_sent_address`) REFERENCES `address` (`id_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I've two tables user and patient. Patient have a user id in it i.e user_id F.K to user.id.
When I create a model classes using reverse engineering, in User model it create Set but in that I need only Patient. My business model says one-to-one relationship between user & patient.
so is there a way to do it ?
Below is table schema.
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `Unique_Email_Id` (`user_email`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `patient` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `PATIENT_EMAIL_ID_UNIQUE_ID` (`email_id`),
UNIQUE KEY `UNIQUE_USER_ID` (`user_id`),
CONSTRAINT `User_Id_Foreign_Key_To_Patient_Id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
hibernate.reveng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-reverse-engineering PUBLIC "-//Hibernate/Hibernate Reverse Engineering DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-reverse-engineering-3.0.dtd" >
<hibernate-reverse-engineering>
<type-mapping>
<sql-type jdbc-type="BIT" hibernate-type="int"></sql-type>
</type-mapping>
<table-filter match-name="user">
<table-filter match-name="patient"></table-filter>
<table name="patient">
<foreign-key constraint-name="Unique_user_Id">
<many-to-one exclude="true"/>
</foreign-key>
</table>
</hibernate-reverse-engineering>
I have solved the problem using a personalized strategy. For this we have to follow the following steps:
1-New dependence on maven:
<!-- Hibernate tools para estrategia presonalizada -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-tools</artifactId>
<version>5.3.7.Final</version>
</dependency>
2-Create custom strategy class (CustomReverseEngineeringStrategy) extending from DelegatingReverseEngineeringStrategy.
package com.xxx.model.strategy;
import java.util.List;
import org.hibernate.cfg.reveng.DelegatingReverseEngineeringStrategy;
import org.hibernate.cfg.reveng.ReverseEngineeringStrategy;
import org.hibernate.cfg.reveng.TableIdentifier;
public class CustomReverseEngineeringStrategy extends DelegatingReverseEngineeringStrategy {
...
}
3-Overwrite excludeForeignKeyAsManytoOne method, where we will set a criteria based on which we will indicate which manytoone relationships must be taken into account and which ones are not:
#Override
public boolean excludeForeignKeyAsManytoOne(String keyname, TableIdentifier fromTable, List<?> fromColumns, TableIdentifier referencedTable, List<?> referencedColumns) {
return referencedTable.getName().startsWith(EXCLUDED_REFERED_TABLE_PREFIX);
}
4-Adapt the configuration 'hibernate code generation' to make use of the customized strategy:
Unfortunately, the exclude annotation does not seem to work.
I hope this solution is useful. Using the personalized strategy can achieve many things because we can overwrite multiple methods that control almost all aspects of the reverse generation of entities.
I am trying to delete all the referenced entity in a collection of a parent entity by setting null on the collection.
For eg :
A is the parent class having one-to-many relation with class B.
Class A {
private Set<B> setB = new HashSet<B>();
}
The mapping is as follows :
<set name="setB " table="B" cascade="save-update" inverse="true">
<key column="FKey"></key>
<one-to-many class="B" />
</set>
a.setB(null);//a is persistent instance of A
The above call to set the collection as null is not deleting the entries in B. Has this anything to do with inverse = true.
Is it illegal to delete the child entities this way?
What about a.getB().clear();?
How to do cascade save or update using hql query in hibernate? also how can i use cascade update for some specific fields instead of updating all child table fields?
for example table User has
userName varchar(10)
Password varchar(10)
table UserAccessRights has
username varchar(10) FK of User table
password varchar(10) FK of User table
Authpassword varchar(10)
cascade update should happen only for username not for password. how can i acheive this?
You don't.
Since the cascade is a Hibernate configuration matter (i.e. HBM or annotations) the data must be returned to Java for processing of cascades. Doing UPDATE/DELETE row modifications in HQL happens entirely on the SQL server (and the SQL server does not understand JPA cascades, as JPA is a Java API not an SQL API or SQL standard).
You need to specify your cascade setting in your hbm files I suppose.
<set name="columnrecord" cascade="save-update" table="..."...>
<key>
<column name="COLUMN_NAME" not-null="true" />
</key>
<one-to-many class="..." />
</set>
Please help me with this hibernate problem, I'm new to hibernate and still trying to get my head around it. I can't seem to work this issue out. I imagine I'm missing something pretty simple.
I've followed the example here to achieve a many-to-one mapping, as my requirements are almost identical: http://www.coderanch.com/t/217519/ORM/java/Hibernate-Newbie-Many-Relation-Tutorial
Please note that when I try to persist the Picture object, the user variable is (at that point in time) empty, as is every other variable bar image.
Also note that I've set hibernate to generate the database schema by itself via config in the hibernate config file.
Here are my mapping files (declarations removed)
User.hbm.xml
<class name="msc.model.User" table="USER">
<id name="id" column="USER_ID">
<generator class="native"/>
</id>
<property name="username"/>
<property name="email"/>
<bag name="pictures"
table="PICTURE"
lazy="true"
cascade="save-update">
<key column="PICTURE_ID"/>
<one-to-many class="msc.model.Picture" />
</bag>
</class>
And Picture.hbm.xml
<class name="msc.model.Picture" table="PICTURE">
<id name="id" column="PICTURE_ID">
<generator class="native"/>
</id>
<property name="story"/>
<property name="tattooist"/>
<property name="pic"/>
<many-to-one name="user"
class="msc.model.User"
column="USER" />
<property name="image" type="blob">
<column name="IMAGE" not-null="true" />
</property>
</class>
The class files (getters and setters stripped)
Picture.java
package msc.model;
import java.io.File;
import java.sql.Blob;
public class Picture {
private Long id = null;
private User user = null;
private File pic = null;
private String story = null;
private String tattooist = null;
private Blob image = null;
}
User.java
package msc.model;
import java.util.ArrayList;
import java.util.List;
public class User {
private Long id = null;
private String username = null;
private String email = null;
private List<Picture> pictures = null;
}
The persistence code (note that bFile is byte stream created from a file):
Session hib_ses = HibernateUtil.getSessionFactory().getCurrentSession();
hib_ses.beginTransaction();
Picture picture = new Picture();
picture.setImage(Hibernate.createBlob(bFile));
Long id = (Long) hib_ses.save(picture);
hib_ses.getTransaction().commit();
Here is the exception:
Cannot add or update a child row: a foreign key constraint fails (`msc`.`picture`, CONSTRAINT `FK85BE8DE2885129D` FOREIGN KEY (`PICTURE_ID`) REFERENCES `user` (`USER_ID`))
Please help!
There is something very strange going on if that is the real error you get.
Cannot add or update a child row: a foreign key constraint fails (`msc`.`picture`, CONSTRAINT `FK85BE8DE2885129D` FOREIGN KEY (`PICTURE_ID`) REFERENCES `user` (`USER_ID`))
This says that PICTURE.PICTURE_ID is a reference to USER.USER_ID. But PICTURE_ID is the PK of picture, which Hibernate will generate upon insertion. Did you mean to create a constraint from PICTURE.USER to USER.USER_ID?
Oh, I see you wrote you generate the schema via Hibernate. I think the error is in your "bag" definition. The key column should not be PICTURE_ID, but USER.
It looks like you are trying to save the Picture before saving the User. Try saving the User first.
[edit]
From the mapping - it looks like there is a Parent/Child relationship between User and Picture.
There is a good example in the Hibernate Documentation for a Parent Child relationship.
If you want the User to be able to be null, then a uni-directional relationship would be better.
[edit]
Another good reference about mapping collections with Hibernate.