Apache Cayenne many-to-many relationship - java

I'm new with Apache Cayenne.
I have only one Entity, called Product.
This entity has a many-to-many relationship with itself, that is a product can contain products, and it can be contained by other products.
I can't model this relationship with Cayenne..
What I do is:
1) I create a table called Composition, with two fields that are both PKs and FKs.
2) I create two toMany from Product to Composition (one from product.id to Composition.contained_id, and one from product.id to Composition.base_id)
This should work with the DB
Now I create only one ObjEntity: Product.
But.. How can I create a flattened relationship?? I'm following this: http://cayenne.apache.org/doc/cayennemodeler-flattened-relationships.html but maybe because it is a relationship with itself I cannot select an Entity in "Target" combo box..
Thank you
Francesco
EDIT: the target checkbox problem there is also if the two entities are different. Cayenne Modeler v.3.0.2

"Target" combo is empty when you select the first relationship, simply because there's no ObjEntity for the join table. But if you keep selecting the next path component, "Product" will appear in the combobox. I wish we redesign this UI for better clarity, but it still works now. See the DataMap XML sample below. I just created it with 3.0.2 Modeler.
Hope this helps.
<?xml version="1.0" encoding="utf-8"?>
<data-map xmlns="http://cayenne.apache.org/schema/3.0/modelMap"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://cayenne.apache.org/schema/3.0/modelMap http://cayenne.apache.org/schema/3.0/modelMap.xsd"
project-version="3.0.0.1">
<db-entity name="composition">
<db-attribute name="BASE_ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
<db-attribute name="CONTAINED_ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
</db-entity>
<db-entity name="product">
<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
<db-attribute name="NAME" type="VARCHAR" length="255"/>
</db-entity>
<obj-entity name="Product" dbEntityName="product">
<obj-attribute name="name" type="java.lang.String" db-attribute-path="NAME"/>
</obj-entity>
<db-relationship name="base" source="composition" target="product" toMany="false">
<db-attribute-pair source="BASE_ID" target="ID"/>
</db-relationship>
<db-relationship name="contained" source="composition" target="product" toMany="false">
<db-attribute-pair source="CONTAINED_ID" target="ID"/>
</db-relationship>
<db-relationship name="base" source="product" target="composition" toDependentPK="true" toMany="true">
<db-attribute-pair source="ID" target="BASE_ID"/>
</db-relationship>
<db-relationship name="contained" source="product" target="composition" toDependentPK="true" toMany="true">
<db-attribute-pair source="ID" target="CONTAINED_ID"/>
</db-relationship>
<obj-relationship name="base" source="Product" target="Product" deleteRule="Deny" db-relationship-path="contained.base"/>
<obj-relationship name="contained" source="Product" target="Product" deleteRule="Deny" db-relationship-path="base.contained"/>
</data-map>

Related

Will the entries be duplicated?

Let say I have a many-to-many relation in spring-data-jpa between Post entity and Post_Tag entity.
Now if I persist a post with tags like java, testing. The post_tags java, testing will be persisted with the post with a cascade type of persist. Now if I save another post with tags like php, testing, will the testing post_tag row duplicated in the Post_Tag table? Or the previous entry will be used?
It depend upon id. If you have field tagName in Post_Tag entity and used it as id then only it will use previous entry else it will create a duplicate entry.
<id name="tagName" type="string">
<column name="tag_name" />
<generator class="assigned" />
</id>

Storing a common value for the 'employee code' while employing inheritance

There are 3 pojo namely EmployeeMaster (the parent class), Hr and Personal (the child class of EmployeeMaster).
Then I have 3 separate tables for each pojo, constructed as :
create table emp_master(emp_code integer,emp_name text,emp_desig text,
emp_dept text,primary key(emp_code));
create table hr(emp_code integer,salary integer,hra integer,da integer,
taxes integer,grade text,foreign key(emp_code) references emp_master(emp_code));
create table personal(emp_code integer,address text,married bool,
foreign key(emp_code) references emp_master(emp_code));
emp_code is the primary key for emp_master and it is the foreign key for both hr and personal table.
I constructed 3 separate jsp forms to take in the data.
Following is the hibernate mapping file :
<hibernate-mapping>
<class name="pojo.EmployeeMaster" table="emp_master">
<id name="emp_code">
<generator class="assigned" />
</id>
<property name="emp_dept" />
<property name="emp_desig" />
<property name="emp_name" />
<joined-subclass name="pojo.Hr" table="hr">
<key column="emp_code" />
<property name="da" />
<property name="grade" />
<property name="hra" />
<property name="salary" />
<property name="taxes" />
</joined-subclass>
<joined-subclass name="pojo.Personal" table="personal">
<key column="emp_code" />
<property name="address" />
<property name="married" />
</joined-subclass>
</class>
</hibernate-mapping>
Now the problem is,I do not want any null row and want to submit data for the same emp_code into other tables namely hr and personal.
But as I try to submit data into the hr table for emp_code 101, that already exists in the master table I get an error saying Duplicate entry '101' for key 'PRIMARY'.
If I change the generator class to increment in the mapping xml, I get a row with null values,for the data inserted from the child class.What I want is an entry into the three tables with the same employee code. How do I do this ?
Your hibernate mapping suggests that you EmployeeMaster, HR and personal forms a hierarchy, i.e. EmployeeMaster is parent class, while HR and Personal are child classes.
There is something wrong in what you want to achieve and what your mapping suggesting. In above mapping, an ID can be mapped to either HR or to Personal and not both.
You should consider changing the mapping from INHERITANCE to ASSOCIATION.
As per your mapping xml, your java classes should be defined as follows:
public class EmployeeMaster {}
public class HR extends EmployeeMaster{}
public class Personal extends EmployeeMaster{}
Now, you have to device how you can fit your requirements into above model. If that is not the case, then you have no choice but to change the model (xml mapping as well)

How to query composite element of collection? (cannot create element join for a collection of non-entities)

I have a class Goods
<class name="Goods">
...
<map name="names" lazy="false" fetch="join">
<key not-null="true" />
<map-key column="LANGUAGE_CODE" type="language" length="2"/>
<composite-element class="Goods$Names">
<property name="name" not-null="true" type="text"/>
<property name="description" type="text"/>
</composite-element>
</map>
...
</class>
The problem arises when I try to search Goods by name like this:
session.createQuery("select g from Goods g where g.names[:lang].name = 'Some goods name'")
javax.servlet.ServletException: java.lang.IllegalArgumentException: Cannot create element join for a collection of non-entities!
Is it really impossible to do so with Hibernate?
Do I really need to make goods name an entity with it's own id?
Any solution?
If you want to create the entity class with composite key, then you have to go for inner class in java, If you are making the entities in hibernate than you have to maintain the relationship(like many to many, one to many, one to one, many to one) between your entities, and then Hibernate have the functionality to perform the join internally, you need not to perform that on you own, this is the whole hint from my side,please try it even this makes your design good understandable to others.

force hyperjaxb2 to generate a fetch="select" propertie on hibernate mapping (on <join> tag)

We have found a problem on our application (a select query that join too much tables). The solution of this problem is to set fetch="select" on the <join> tag in our hibernate mapping file.
<subclass extends="...">
<join table="MyTable" fetch="select">
...
</join>
</subclass>
But this mapping is generated by hyberjaxb2. I haven't found how to add the fetch attribute into the <join> tag. I can change the target table name (ie: MyTable) using this :
<hj:table name="MyTable"/>
but if I change it to
<hj:table name="MyTable" fetch="select" />
nothing is added in my mapping file.
So, how can I add the fetch propertie on the <join> tag using hyperjaxb2 ?
Thank you.
taken from hyperjaxb2 reference documentation :
https://hyperjaxb2.dev.java.net/nonav/hyperjaxb2/reference/reference.html#d0e850
In case you XML Schema construct maps onto a table, you can customize table name, schema or catalog
with a table customization element.
So I can't add my fetch attribut...

Combining delete-orphan with a where condition

Hibernate mapping question where the behavior is ambiguous and/or dangerous. I have a one-to-many relationship that has a cascade-delete-orphan condition AND a where condition to limit the items in the collection. Mapping here -
<hibernate-mapping>
<class name="User" table="user" >
<!-- properties and id ... -->
<set table="email" inverse="true" cascade="all,delete-orphan" where="deleted!=true">
<key column="user_id">
<one-to-many class="Email"/>
</set>
</class>
</hibernate-mapping>
Now suppose that that I have a User object which is associated to one or more Email objects, at least one of which has a 'true' value for the deleted property. Which of the following two will happen when I call session.delete() on the User object?
The User and all the Email objects, including those with deleted=true, are deleted
The User and the Email objects that are deleted!=null are deleted.
On one hand, scenario 1) ignores the where condition, which may not be correct according to the domain model. BUT in scenario 2) if the parent is deleted, and there's a foreign key constraint on the child (email) table's join key, then the delete command will fail. Which happens and why? Is this just another example of how Hibernate's features can be ambiguous?
I didn't test the mapping but in my opinion, the correct (default) behavior should be to ignore the where condition and to delete all the child records (that's the only option to avoid FK constraints violations when deleting the parent). That's maybe not "correct" from a business point of view but the other option is not "correct" either as it just doesn't work.
To sum up, the mapping itself looks incoherent. You should either not cascade the delete operation (and handle the deletion of the child Email manually).
Or, and I think that this might be the most correct behavior, you should implement a soft delete of both the User and associated Email. Something like this:
<hibernate-mapping>
<class name="User" table="user" where="deleted<>'1'">
<!-- properties and id ... -->
<set table="email" inverse="true" cascade="all,delete-orphan" where="deleted<>'1'">
<key column="user_id">
<one-to-many class="Email"/>
</set>
<sql-delete>UPDATE user SET deleted = '1' WHERE id = ?</sql-delete>
</class>
<class name="Email" table="email" where="deleted<>'1'">
<!-- properties and id ... -->
<sql-delete>UPDATE email SET deleted = '1' WHERE id = ?</sql-delete>
</class>
</hibernate-mapping>
What is done here:
We override the default delete using sql-delete to update a flag instead of a real delete (the soft delete).
We filter the entities and the association(s) using the where to only fetch entities that haven't been soft deleted.
This is inspired by Soft deletes using Hibernate annotations. Not tested though.
References
5.1.3. Class
6.2. Collection mappings
16.3. Custom SQL for create, update and delete

Categories