How do I create a map property that has both key column and a map key as parts of PK of the detail class?
Like this:
<class entity-name="Person" >
<id name="id"/>
<property name="birthDate" type="date"/>
<map name="names">
<key column="personId"/>
<map-key type="string" column="code"/>
<one-to-many class="PersonName" />
</map>
</class>
<class entity-name="PersonName">
<composite-id>
<key-many-to-one name="personId" class="Person"/>
<key-property name="code" type="string" length="32"/>
</composite-id>
<property name="lastName" type="string" length="64" index="nameSearch"/>
<property name="firstName" type="string" length="64" index="nameSearch"/>
<property name="middleName" type="string" length="64" index="nameSearch"/>
<property name="isActual" type="boolean"/>
</class>
I need to avoid creating a surrogate key in PersonName class that would require a special handling. Json shown below should be automatically persisted in the DB, inserting and updating detail record when necessary, based on PersonId-code key.
It's natural that "code" property identifies the row together with the person Id.
I tried different combinations of "inverse", "not-null" etc. I admit that I don't fully get how it works. I get different error messages, like:
ERROR: null value in "person" column violates NOT NULL constraint
Details: Erroreous row contains (null, null, lastname, ffffirst, null, null).
or:
org.hibernate.MappingException: Repeated column in mapping for entity:
PersonName column: person (should be mapped with insert="false" update="false")
It should look like this, if represented as json:
{
"birthDate": "33-44-55",
"names": {
"mainName": {
"lastName": "lastname",
"firstName": "ffffirst"
},
"maidenName": {
"lastName": "lastname1",
"firstName": "ffffirst2"
},
"old": {
"lastName": "lastname3",
"firstName": "ffffirst4"
}
}
}
UPD (clarification of question)
Actually this is a main goal - the system should create master and all details when posted with this json. This json is converted into the mapped classes. If master has an "id" populated, the system should update both master and details, adding new entries when needed. Details ("names" map entries) should not have any surrogate "id" specified. They are identified by master's "personId" field and "code" field.
** clarification 2 **
As shown in the example json, the map key is a string, not a composite key. As seen in mapping xml, this is all dynamic mapping. No additional class, that couldn't be instantiated automatically from the json, should be written.
Hope it's possible!
To get the whole picture, the table is generated OK, I like it:
CREATE TABLE personname
(
personid character varying(32) NOT NULL,
code character varying(32) NOT NULL,
lastname character varying(64),
firstname character varying(64),
middlename character varying(64),
isactual boolean,
CONSTRAINT personname_pkey PRIMARY KEY (personid, code),
CONSTRAINT fk_1skg5frawyftx8co9uawhc3r8 FOREIGN KEY (personid)
REFERENCES person (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
I think your mapping should look like this:
You need a nested Key class (e.g. Person$Key, although it can be an outer class too) for the embedded composite id:
public static class Key {
private Long personId;
private String code;
public Key(Long personId, String code) {
this.personId = personId;
this.code = code;
}
public Long getPersonId() {
return personId;
}
public String getCode() {
return code;
}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Key)) {
return false;
}
Key k = (Key)obj;
return personId.equals(k.personId) && code.equals(k.code);
}
#Override
public int hashCode() {
return 37*personId.hashCode() + code.hashCode();
}
}
Then your map will be like:
private Map<Key,PersonName> names = new HashMap<Key,PersonName>();
The Hibernate mapping:
<class entity-name="Person" >
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="birthDate" type="date"/>
<map name="names" inverse="true">
<composite-map-key class="Person$Key">
<key-property name="personId"/>
<key-property name="code"/>
</composite-map-key>
<one-to-many class="PersonName" />
</map>
</class>
<class entity-name="PersonName">
<composite-id name="id" class="Person">
<key-property name="personId"/>
<key-property name="code"/>
</composite-id>
<many-to-one name="person" class="Person"
insert="false" update="false">
<column name="personId"/>
<column name="code"/>
</many-to-one>
<property name="lastName" type="string" length="64" index="nameSearch"/>
<property name="firstName" type="string" length="64" index="nameSearch"/>
<property name="middleName" type="string" length="64" index="nameSearch"/>
<property name="isActual" type="boolean"/>
</class>
It's impossible. Use mongodb or write your own ORM.
Related
I have trying to read from my database using Hibernate but I am having a problem. Any help would be appreciated. It has to do with the fact that there is a set of Answers in a Question object and I'm not sure the implications this is having on the subject.
List<Question> questions = session
.createQuery("from Question where topic = :id")
.setParameter("id", topic)
.list();
Code:
<class name="cdd.model.Question" table="question">
<id column="question_id" name="questionID" type="org.hibernate.type.PostgresUUIDType">
<generator class="org.hibernate.id.UUIDGenerator"/>
</id>
<many-to-one class="cdd.model.User" column="submitted_by" name="submittedBy" not-null="true" lazy = "false"/>
<many-to-one class="cdd.model.Topic" column="question_topic" name="topic" not-null="true" lazy = "false"/>
<property column="title" name="title" type="org.hibernate.type.TextType"/>
<property column="correct_answer" name="correctAnswer" type="org.hibernate.type.TextType"/>
<property column="date_submitted" name="dateSubmitted" type="org.hibernate.type.TimestampType"/>
<property column = "approved" name = "approved" type = "org.hibernate.type.BooleanType"/>
<set cascade="all" name="answers" >
<key column="question_id" not-null="true" lazy = "false" />
<one-to-many class="cdd.model.Answer"/>
</set>
</class>
Java Class That was mapped:
public class Question {
UUID questionID;
User submittedBy;
Topic topic;
String title;
String correctAnswer;
Timestamp dateSubmitted;
boolean approved;
Set<Answer> answers;
/*Constructor, Getters and Setters*/
}
Error:
XML validation started.
Checking file:/Users/rhamblin/Desktop/CDDExamGen/src/hibernateMapping.hbm.xml...
Attribute "lazy" must be declared for element type "key". [69]
XML validation finished.
I am trying to check if a row exists in my database.
Word word = (Word) session.createQuery("select 1 from Word w where w.content = :key").setParameter("key",words[i]).uniqueResult();
I'm also trying:
Word word = session.get(Word.class,contentId);
Besides that I tried session.load,and some others. Everytime Hibernate returns error:
Error indexing: null
or
Error indexing: no row with the given identifier exists.
It is true, row does not existing but why doesn't it just returns null like it should for session.get:
http://docs.jboss.org/hibernate/orm/3.5/javadocs/org/hibernate/Session.html#get(java.lang.Class,%20java.io.Serializable).
In case of not finding a row I wanted to create one and add to a database but I'm not able to check if it exists.
EDIT:
Word.java
public class Word {
private String content;
private Set<Sentence> sentences;
empty constructor, setters and getters
}
Word.hbm.xml
<class name="Word">
<id name="content" column="wordId" type="string">
</id>
<set name="sentences" inverse="true">
<key><column name="wordId"/></key>
<many-to-many class="Sentence" column="sentenceId"/>
</set>
</class>
Sentence.java
public class Sentence {
private long id;
private ProcessedUrl processedUrl;
private List<Word> words;
empty constructor, setters and getters
}
Sentence.hbm.xml
<class name="Sentence">
<id name="id" column="sentenceId">
<generator class="native"/>
</id>
<many-to-one name="processedUrl" column="processedUrlId" not-null="true"/>
<list name="words">
<key>
<column name="sentenceId" not-null="true"/>
</key>
<list-index column="idx" />
<many-to-many class="Word">
<column name="wordId" not-null="true"/>
</many-to-many>
</list>
</class>
I have two tables in a one-to-many relationship mapped by Hibernate:
<hibernate-mapping>
<class name="Class1" table="TABLE_1" dynamic-update="true">
<meta attribute="implement-equals">true</meta>
<cache usage="read-write"/>
<id name="id" column="KEY_1" length="5" >
....
</id>
<property name="property1a" column="COLUMN_1A" type="string" length="10" />
<property name="property1b" column="COLUMN_1B" type="string" length="10" />
<set name="properties2Set" table="TABLE_2" lazy="true" fetch="subselect" cascade="all">
<key column="FOREIGN_KEY_1"/>
<composite-element class="Class2">
<property name="property2a" type="string" column="COLUMN_2A"/>
<property name="property2b" type="string" column="COLUMN_2B"/>
<property name="property2c" type="string" column="COLUMN_2C"/>
</composite-element>
</set>
</class>
</hibernate-mapping>
Class1 looks like:
public class Class1
{
//... Here the definitions of property1a, property1b
private Set<Class2> properties2Set = new HashSet<Class2>();
public Set<Class2> getProperties2Set()
{
return properties2Set;
}
public void setProperties2Set(Set<Class2> properties2Set)
{
this.properties2Set = properties2Set;
}
// ... Definitions of equals() and hashCode()
}
Class2 contains properties property2a, property2b, property2c. There is a constraint
CONSTRAINT PK_OLD PRIMARY KEY (FOREIGN_KEY_1, COLUMN_2A, COLUMN_2B, COLUMN_2C)
I want to add to the table the column
COLUMN_ID_NUMBER NUMBER(5) DEFAULT 0 NOT NULL
change the constraint into
CONSTRAINT PK_NEW PRIMARY KEY (FOREIGN_KEY_1, COLUMN_ID_NUMBER)
and generate values of COLUMN_ID_NUMBER in order not to violate the constraint.
Where to add the property that would be mapped into COLUMN_ID_NUMBER and how to generate its values? Is it possible to leave the structure of Class2 unchanged?
I know that it is a trite question, but I could not find a solution. I have two beans and one of them has HashMap collection. I'm getting an exception when trying to read this collection. Mapping config had been specified to load this collection eagerly.
My environment is :
Hibernate 4.2.0
mysql-connector-java 5.1.24
Also I have two beans:
public class FeaturedDoc {
private Long id;
private Map<Feature, Float> features;
public FeaturedDoc() {
features = new HashMap<Feature, Float>();
}
(getters and setters)
}
and
public class Feature {
private Long id;
private String name;
private Long internalId;
(getters and setters)
}
This beans have mapping:
<class name="Feature" table="FEATURE">
<id name="id" type="long" column="ID">
<generator class="increment"/>
</id>
<property name="name" length="255" type="string" unique="true" column="NAME" index="INDEX_NAME"/>
<property name="internalId" type="long" unique="true" not-null="false" column="INTERNAL_ID" index="INDEX_INTID"/>
<sql-insert>insert into FEATURE (NAME, INTERNAL_ID, ID) values (?, ?, ?) on duplicate key update ID = ID</sql-insert>
</class>
<class name="FeaturedDoc" table="FEATURED_DOC">
<id name="id" type="long" column="ID">
<generator class="increment"/>
</id>
<map name="features" table="DOC_FEATURE" cascade="all" lazy="false" fetch="join">
<key column="ID"></key>
<map-key-many-to-many column="FEATURE_ID" class="Feature"/>
<element column="value" type="float"/>
</map>
</class>
Also I have DAO layer with method:
public FeaturedDoc read(long id) {
FeaturedDoc fd = null;
try {
session.beginTransaction();
fd = session.get(FeaturedDoc.class, id);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
close();
}
return fd;
}
When I'm trying to do something like this:
FeaturedDoc fd = daoService.read(26);
System.out.println(fd.getFeatures());
I'm getting an exception
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
Do you know how should I fix this error?
Assuming that everything else is okay (in the mappings), have you tried putting lazy="false" before cascade="all". I found that this was a problem in my mapping which resulted in this LazyInitializationException error.
This ordering is shown in the following Hibernate Reference: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/collections.html
I have solved this problem! The reason was in Feature class. It had not hashCode and equals functions. After implementation of these functions everything has become ok.
I have the following parent object which maps to a table in my database:
public Parent {
private Long id;
private String mid;
private Integer days;
private BigDecimal fee;
private DateTime createdDate = new DateTime();
private DateTime lastModifiedDate;
private Map<String, Child> children;
}
With the following .hbm.xml:
<hibernate-mapping default-access="field">
<class name="Parent" table="parent_table">
<id column="id" length="50" name="id" unsaved-value="null">
<generator class="increment"/>
</id>
<property length="50" name="mid"/>
<property name="days"/>
<property name="fee"/>
<property name="createdDate" type="(...)PersistentDateTime"/>
<property name="lastModifiedDate" type="(...)PersistentDateTime"/>
<map cascade="all-delete-orphan" inverse="true" name="children" >
<key column="parentId" />
<map-key column="country" type="string" />
<one-to-many class="Child" />
</map>
</class>
</hibernate-mapping>
The child object is as follows:
public class Child implements Serializable {
private Long parentId;
private String country;
private String cu;
}
With the following .hbm.xml:
<hibernate-mapping default-access="field">
<class name="Child" table="child_table">
<composite-id>
<key-property name="parentId"/>
<key-property name="country"/>
<key-property name="cu"/>
</composite-id>
</class>
</hibernate-mapping>
After acquiring a Parent object from my db via:
getSession().createCriteria(Parent.class)
.add(Restrictions.eq("mid", mid))
.uniqueResult();
After making some changes to Child.cu in the children map I call a saveOrUpdate on the Parent object. After doing so all appears to save / update fine but upon checking the child_table in the db, these changes have not been saved / updated.
I believe this has something to do with the mappings of the map in the Parent class but can't seem to figure it out. Any help would be appreciated.
Thanks in advance.
If I understand correctly, you're modifying a field which is part of the primary key of your entity. This is illegal: the ID should be immutable.
My advice is to follow the good practices: use a non-composite, purely technical, auto-generated primary key. Everything will be much simpler (and also faster).