one-to-many relationshipt with database constrain and inverse=true - oracle

There are two classes A and B and hibernate mappings
<hibernate-mapping default-lazy="false">
<class name="A" table="A">
<id name="id" type="long">
<generator class="sequence"><param name="sequence">A_SEQUENCE</param></generator></id>
<set name="a" cascade="all" inverse="false" >
<key><column name="A_FK" not-null="true" /></key>
<one-to-many class="B" /></set>
</class>
</hibernate-mapping>
<hibernate-mapping default-lazy="false">
<class name="B" table="B">
<id name="id" type="long"> <column name="ID"/>
<generator class="sequence"><param name="sequence">B_SEQUENCE</param></generator></id>
</class>
</hibernate-mapping>
On the database there is a not null contraint and a foreign key constraint on the column A_FK of table B.
When I try to insert an A that contains a B I get the following error:
ORA-01400: cannot insert NULL into ("SCHEMA"."B"."A_FK")
Is it possible to insert this kind of data without having to specify the inverse=true flag? and the inverse relationship?

Not without getting rid of the way the id is generated. Can you switch how the Id is generated?

Converting the problem to a question is half the answer...
What was missing was the not-null="true" on the key of the set:
<set name="a" cascade="all" inverse="false" >
<key not-null="true"><column name="A_FK" not-null="true" /></key>
<one-to-many class="B" />
</set>

Related

spring-boot with liquibase #OneToMany mapping

I have two entity Person and Address. And Person can have multiple Address.
<createTable tableName="ADDRESS">
<column name="id" type="bigint(20)" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
... //columns
</column>
</createTable>
<createTable tableName="PERSON">
<column name="id" type="bigint(20)" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
... //columns
</column>
</createTable>
<addForeignKeyConstraint
constraintName="fk_constraint_worker_phone_number"
referencedTableName="CONTACT_NUMBER" baseColumnNames="ContactNumbers"
baseTableName="WORKER" referencedColumnNames="id" />
I want 3rd table (like hibernate generate in #OneToMany mapping).
How to do this with liquibase-springboot?
If the relation is truly a OnToMany, you don't need a 3rd table. Simply, add PrimaryKeyJoinColumn.
If the address can be reused for many persons, it's a ManyToMany relation.
You can use #ManytoMany and add information about you joined table un #jointable
Well, in case of liquibase we have to create the 3rd table manually and have to apply the necessary constraints.
Create the table which manages the mapping :
<createTable tableName="PERSON_ADDRESS">
<column name="PERSON_ID" type="BIGINT">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="ADDRESS_ID" type="BIGINT">
<constraints primaryKey="true" nullable="false" />
</column>
</createTable>
Apply the constraints:
1) Ensure that Persons id is unique in the mapping table
2) A foreign key relationship between ADDRESS's id and PERSON_ADDRESS's PERSON_ID
3) A foreign key relationship between PERSON's id and PERSON_ADDRESS's ADDRESS_ID
<addUniqueConstraint
columnNames="PERSON_ID" tableName="PERSON_ADDRESS"
constraintName="UK_PHONE_NUMBERS_ID" />
<addForeignKeyConstraint
constraintName="FK_ADDRESS_PERSON_ADDRESS"
referencedTableName="ADDRESS"
baseColumnNames="ADDRESS_ID"
baseTableName="PERSON_ADDRESS" referencedColumnNames="id" />
<addForeignKeyConstraint
constraintName="FK_PERSON_PERSON_ADDRESS"
referencedTableName="PERSON"
baseColumnNames="PERSON_ID"
baseTableName="PERSON_ADDRESS" referencedColumnNames="id" />

Lazy loading hibernate

I use Spring 3 and hibernate 3.6.3.Final. I have to entities A and B. The hbm files are:
A.hbm.xml
<hibernate-mapping>
<class name="com.A" table="A" >
<id name="A_Id" type="string">
<column name="Id" length="100" />
<generator class="foreign">
<param name="property">B</param>
</generator>
</id>
<one-to-one name="B" class="B" constrained="true" fetch="select" ></one-to-one>
<property>...</property>
</class>
</hibernate-mapping>
and B.hbm.xml
<hibernate-mapping>
<class name="com.B" table="B">
<id name="B_Id" type="string">
<column name="Id" length="32" />
<generator class="assigned" />
</id>
...
<one-to-one name="some_A" class="com.A" lazy="proxy"></one-to-one> // FIRST
<one-to-one name="other_A" class="com.A" lazy="proxy" cascade="all" > // SECOND
<formula>other_BSRC_REG_REP_MESSAGE_ID</formula>
</one-to-one>
</class>
</hibernate-mapping>
From #Service I use the following code to get entity B by id:
getHibernateTemplate().get(getEntityClass(), id)
I enabled the show_sql mode in hibernate in order to check if my lazy loading works fine. Unfortunately I don't understand them
When either FIRST or SECOND line is commented out hibernate shows exactly one query (where all of the one-to-one associations are present as join),
When both FIRST and SECOND lines are uncommented hibernate shows two queries, one to get B and second to get an A entity.
Why this happens? Is it because all of the one-to-one association can't be lazy loaded so either they are eagerly loaded as a separate query or "joined" to the original query? If so, why hibernate didn't joined on the A table twice?

How-to include an optional Bag in my hbm.xml file?

How do I make the Bag optional for class Test in the following pseudo hbm.xml?
<class name="Test" table="test">
<bag name="bag" table="example" cascade="all" fetch="join">
<key property-ref="key">
<column name="a_id" />
<column name="b_id" />
</key>
<element column="example_id"
type="my.myclass"/>
</bag></class>
my.mclass is a custom type (my.myclass implements org.hibernate.usertype.UserType)
In the moment if there is no fitting "test example" row in the example table I get an exception?
(I was hoping to find a kind of not-found attribute? But there is no)
What's the relationship between Test and myclass? 1 to many or many to many?
If it's 1 to many in your case, I will suggest you create separate mapping for myclass and use following mapping for Test class
<bag name="bag" table="example" inverse="true" cascade="all" fetch="join">
<key property-ref="key">
<column name="a_id" />
<column name="b_id" />
</key>
<one-to-many class="my.myclass"/>
</bag>

Mapping a composite primary key with a foreign key relation in Nhibernate

a company I am working for is using an ERP and it's legacy database is Oracle.
Until now I've used packages (oracle stored procedures) to access data but during the years the number has grown consistently and now I can't manage them anymore.
I was trying to do some experiments with Nhibernate and started mapping few tables.
All the tables have composite primary keys.
A brief description:
Table Order (table name: OCSAORH)
OCHORDN (PK) => OrderNumber
OCHAMND (PK)
OCHCOSC (PK) => Company
OCHCLII
...
Table OrderLine (table name: OCSALIN)
OCLORDN (PK) => OrderNumber
OCLAMND (PK)
OCLCOSC (PK) => Company
OCLLINN (PK) => Line Number
OCLSSEQ (PK)
OCLITMN
...
This is my mapping
Order:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MvcOracleNhibernate"
namespace="MvcOracleNhibernate.Domain">
<class name="Order" table="OCSAORH">
<composite-id>
<key-property name="Number" column="OCHORDN"></key-property>
<key-property name="Ver" column="OCHAMND"></key-property>
<key-property name="Company" column="OCHCOSC"></key-property>
</composite-id>
<property name="CustomerCode" column="OCHCLII" type="String" length="10"></property>
<property name="Reference" column="OCHOCNO" type="String" length="25"></property>
<property name="Date" column="OCHOCDT" type="Double"></property>
<bag name="OrderLines" cascade="all-delete-orphan" generic="true" inverse="true" lazy="false">
<key>
<column name="OCLORDN" not-null="true"/>
<column name="OCLAMND" not-null="true"/>
<column name="OCLCOSC" not-null="true"/>
</key>
<one-to-many class="OrderLine" not-found="ignore"/>
</bag>
</class>
</hibernate-mapping>
OrderLine:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MvcOracleNhibernate"
namespace="MvcOracleNhibernate.Domain">
<class name="OrderLine" table="OCSALIN">
<composite-id>
<key-property name="Number" column="OCLORDN"></key-property>
<key-property name="Ver" column="OCLAMND" ></key-property>
<key-property name="Company" column="OCLCOSC"></key-property>
<key-property name="Line" column="OCLLINN"></key-property>
<key-property name="Seq" column="OCLSSEQ"></key-property>
</composite-id>
<property name="Item" column="OCLITMN" type="String" length="19"></property>
<property name="Quantity" column="OCLQTYP" type="Double"></property>
</class>
</hibernate-mapping>
With these mappings everything works fine; I can load an order and the lazy loading loads my lines.
While reading some documentation I've noticed that I haven't defined the many-to-one relation so I've added this:
<many-to-one name="Order" class="Order" lazy="proxy">
<column name="OCHORDN" not-null="true"/>
<column name="OCHAMND" not-null="true"/>
<column name="OCHCOSC" not-null="true"/>
</many-to-one>
to the OrderLine mapping file.
Now if I run my test app the order is loaded properly but the order lines are not loaded.
I get a {NHibernate.ADOException} = {"could not initialize a collection: ... }
I've tried to investigate and noticed that the generated query to retrieve the rows is wrong. This is the SQL:
SELECT
orderlines0_.OCLORDN as OCLORDN1_,
orderlines0_.OCLAMND as OCLAMND1_,
orderlines0_.OCLCOSC as OCLCOSC1_,
orderlines0_.OCLLINN as OCLLINN1_,
orderlines0_.OCLSSEQ as OCLSSEQ1_,
orderlines0_.OCLORDN as OCLORDN13_0_,
orderlines0_.OCLAMND as OCLAMND13_0_,
orderlines0_.OCLCOSC as OCLCOSC13_0_,
orderlines0_.OCLLINN as OCLLINN13_0_,
orderlines0_.OCLSSEQ as OCLSSEQ13_0_,
orderlines0_.OCLITMN as OCLITMN13_0_,
orderlines0_.OCLQTYP as OCLQTYP13_0_,
orderlines0_.OCHORDN as OCHORDN13_0_,
orderlines0_.OCHAMND as OCHAMND13_0_,
orderlines0_.OCHCOSC as OCHCOSC13_0_
FROM OCSALIN orderlines0_
WHERE
orderlines0_.OCLORDN=?
and orderlines0_.OCLAMND=?
and orderlines0_.OCLCOSC=?
As you can notice the last 3 fields of the select (those with the prefix OCH instead of OCL) aren't members of the OCSALIN table; they are the key of the OCSAORH.
After 1 days spent reading documentation and examples I can't figure out what I am doing wrong.
Is there anybody there who can try to help?
This is the expected behavior. You're defining the foreign keys in your many-to-one mapping. So the columns need to exist in the object you're defining.
I think you want this
<many-to-one name="Order" class="Order" lazy="proxy">
<column name="OCLORDN" not-null="true"/>
<column name="OCLAMND" not-null="true"/>
<column name="OCLCOSC" not-null="true"/>
</many-to-one>

Error 500: could not initialize a collection in hibernate mapping

I am a newbie for Hibernate. Hope you guys can help me debug below error which really make me crazy.
I got a table called CONTENT_WORKGROUP which will map to another table called CONTENT_WORKGROUP_ROLE. Below is the table structure and sample data:
CONTENT_WORKGROUP
CM_WORKGROUP_ID NUMBER(15,0)
WORKGROUP_ID NUMBER(15,0)
ROLE_ID VARCHAR2(20 BYTE)
CONTENT_WORKGROUP_ROLE
CM_WORKGROUP_ROLE_ID NUMBER(15,0)
ROLE_ID VARCHAR2(20 BYTE)
FUNCTION_ID VARCHAR2(40 BYTE)
P/S: One user workgroup can have multiple role (Creator, Admin, Approver). The function(Add, Edit, Delete) that can perform by this workgroup can be query from CONTENT_WORKGROUP_ROLE.
Sample DATA:
CONTENT_WORKGROUP
CM_WORKGROUP_ID WORKGROUP_ID ROLE_ID
1 136 Creator
2 137 Administrator
3 136 Administrator
CONTENT_WORKGROUP_ROLE
CM_WORKGROUP_ROLE_ID ROLE_ID FUNCTION_ID
1 Creator Copy
2 Creator Edit
3 Creator Delete
4 Creator Add
5 Administrator Edit
6 Administrator Approve
7 Administrator Reject
However, I am getting the error when I get the SET of ContentWorkgroupRole hold by particular workgroup.
[11/23/10 15:28:56:053 SGT] 00000039 SystemOut O [23/11/2010 15:28:56.053] ERROR JDBCExceptionReporter - ORA-01722: invalid number
[11/23/10 15:28:56:100 SGT] 00000039 ServletWrappe E SRVE0068E: Uncaught exception thrown in one of the service methods of the servlet: action. Exception thrown : javax.servlet.ServletException: could not initialize a collection: [corp.celcom.next.infochannel.model.ContentWorkgroup.contentWorkgroupRole#1]
Below is my hibernate mapping file:
ContentWorkgroup.hbm.xml
<hibernate-mapping>
<class name="corp.celcom.next.infochannel.model.ContentWorkgroup" table="CM_WORKGROUP" >
<id name="cmWorkgroupId" type="long">
<column name="CM_WORKGROUP_ID" precision="15" scale="0" />
CM_WORKGROUP
CM_WORKGROUP_ID
ContentWorkgroupRole.hbm.xml
<hibernate-mapping>
<class name="corp.celcom.next.infochannel.model.ContentWorkgroupRole" table="CM_WORKGROUP_ROLE" >
<id name="cmWorkgroupRoleId" type="long">
CM_WORKGROUP_ROLE_ID
CM_WORKGROUP_ROLE
<many-to-one name="contentWorkgroup" class="corp.celcom.next.infochannel.model.ContentWorkgroup" fetch="select">
<column name="ROLE_ID" precision="15" scale="0" />
</many-to-one>
In my ACTION class, the above mentioned error occured on this line:
Iterator iter = cw.getContentWorkgroupRole().iterator();
for(ContentWorkgroup cw : contentWorkgroupList)
{
Iterator iter = cw.getContentWorkgroupRole().iterator();
while (iter.hasNext()) {
ContentWorkgroupRole role = (ContentWorkgroupRole) iter.next();
if (role.getFunctionId().equalsIgnoreCase(Constant.ADD))
myForm.setAllowAdd(true);
if (role.getFunctionId().equalsIgnoreCase(Constant.EDIT))
myForm.setAllowEdit(true);
if (role.getFunctionId().equalsIgnoreCase(Constant.DELETE))
myForm.setAllowDelete(true);
}
}
The weird part is when I change the ROLE_ID to Integer/Long (i.e 1-Creator, 2-Administrator), instead of using String, it work fine! I couldn't understand why and what the problem on my code.
Thanks for you help. It took me 1 day already to cope with this error. Thanks!
Sorry, the display seem got abit problem. I write again here:
ContentWorkgroup.hbm.xml
<hibernate-mapping>
<class name="corp.celcom.next.infochannel.model.ContentWorkgroup" table="CM_WORKGROUP" >
<id name="cmWorkgroupId" type="long">
<column name="CM_WORKGROUP_ID" precision="15" scale="0" />
<generator class="corp.celcom.next.util.MultipleTableGenerator" >
<param name="KEYTABLE_VALUE">CM_WORKGROUP</param>
<param name="KEYCOLUMN_VALUE">CM_WORKGROUP_ID</param>
</generator>
</id>
<property name="workgroupId" type="long">
<column name="WORKGROUP_ID" precision="15" scale="0" not-null="true" />
</property>
<property name="srId" type="string">
<column name="SR_ID" length="15" not-null="true" />
</property>
<property name="contentCategoryId" type="string">
<column name="CONTENT_CATEGORY_ID" length="40" not-null="true" />
</property>
<property name="roleId" type="string">
<column name="ROLE_ID" length="20" not-null="true" />
</property>
<set name="contentWorkgroupRole" table="CM_WORKGROUP_ROLE" inverse="true">
<key>
<column name="ROLE_ID" length="20" not-null="true" />
</key>
<one-to-many class="corp.celcom.next.infochannel.model.ContentWorkgroupRole" />
</set>
</class>
ContentWorkgroupRole.hbm.xml
<hibernate-mapping>
<class name="corp.celcom.next.infochannel.model.ContentWorkgroupRole" table="CM_WORKGROUP_ROLE" >
<id name="cmWorkgroupRoleId" type="long">
<column name="CM_WORKGROUP_ROLE_ID" precision="15" scale="0" />
<generator class="corp.celcom.next.util.MultipleTableGenerator">
<param name="KEYCOLUMN_VALUE">CM_WORKGROUP_ROLE_ID</param>
<param name="KEYTABLE_VALUE">CM_WORKGROUP_ROLE</param>
</generator>
</id>
<many-to-one name="contentWorkgroup" class="corp.celcom.next.infochannel.model.ContentWorkgroup" fetch="select">
<column name="ROLE_ID" precision="15" scale="0" />
</many-to-one>
<property name="menuId" type="string">
<column name="MENU_ID" length="40" not-null="true" />
</property>
<property name="functionId" type="string">
<column name="FUNCTION_ID" length="40" not-null="true" />
</property>
In my ACTION class, the above mentioned error occured on this line:
Iterator iter = cw.getContentWorkgroupRole().iterator();
for(ContentWorkgroup cw : contentWorkgroupList)
{
Iterator iter = cw.getContentWorkgroupRole().iterator();
while (iter.hasNext()) {
ContentWorkgroupRole role = (ContentWorkgroupRole) iter.next();
if (role.getFunctionId().equalsIgnoreCase(Constant.ADD))
myForm.setAllowAdd(true);
if (role.getFunctionId().equalsIgnoreCase(Constant.EDIT))
myForm.setAllowEdit(true);
if (role.getFunctionId().equalsIgnoreCase(Constant.DELETE))
myForm.setAllowDelete(true);
}
}

Resources