Update one value from a list of dependent objects - java

Given an entity with a list of components:
class Entity{
Long id;
String name;
List<Component> components = new ArrayList<Component>();
}
class Component{ Object value; }
Configuration:
<hibernate-mapping>
<class name="Entity" table="entity">
<id name="id" access="field" column="id"/>
<property name="name" access="field" unique="true"/>
<list name="components" access="field" table="COMPONENTS" lazy="true">
<key column="id"/>
<list-index column="idx"/>
<composite-element class="Component">
<property name="value" access="field"/>
</composite-element>
</list>
</class>
</hibernate-mapping>
Is it possible to update one component from the list with HQL statement like
update Entity e set e.components[:index].value = :value where e.name = :name
that does not work?
Alternatively, is it possible to configure lazy loading of the list of components in a way that the first access:
entity.components.get(0).value = "..";
does not load the whole list?
Edit:
The lazy="extra" configuration does work for select (loads only the component to update), but it will not update the changed component.

You can't update a single collection element via HQL.
From the 13.4. DML-style operations chapter:
There can only be a single entity named in the from-clause.
No joins, either implicit or explicit, can be specified in a bulk HQL query.
Since your collection element is not an entity, it's not addressable from within bulk update. Technically speaking, non-entity collection elements are not addressable in general; indexed collections or sets with elements having natural ids being the only exceptions.
While it is possible to lazy-load collection elements few at a time (though it doesn't really make sense in this case unless you know ahead of time that you'll only be looking at Nth element since batch size is not easily changeable at runtime), it's not going to help because entire collection will be loaded anyway when you try to update it.
Selecting a single collection element is possible for indexed collection (not part of your question, but I wanted to clarify on this based on KLE answer and your comments):
select c
from Entity e join e.components c
where index(c) = :index

Related

Different results with query api vs. hql

I have the following entity in hbm.xml file
<class name="Base" table="base">
<id name="id"/>
<list name="ips" cascade="all-delete-orphan" lazy="false" fetch="join">
<cache usage="read-write" include="all" />
<key column="base_id" />
<list-index column="ip_order"/>
<element column="ip" type="string"/>
</list>
</class>
i have one entity Base with two ips string in the collection.
when i make:
session.createCriteria(base.class).list();
the result is two Base object
when i make:
session.createQuery(" from Base").list();
the result is one entity Base.
can someone tell me why i have this situation?
As per your mapping xml Base is one table and ips(IP) is another table.
One Base having two List(ips) means Base table will have one entry in DB(base table).
IP will have two entries in DB (ip table).
Obvisully Base table will have only one entry.
Check this example
I bet there are 2 records in the table for ips.
As you have declare ips being eager fetched, so it will also join fetch the ips when you are creating the criteria to fetch Base.class, causing the "result set" contains 2 records. However, the two "records" are in fact same instance.
The way to solve is simple though, search for use of DISTINCT_ROOT_ENTITY result transformer.

how do I do the following hibernate mapping?

I have the following DB schema :
table a {
id,
state
}
table b {
id,
a_id,
is_valid,
amount
}
I want to have a hibernate mapping where I fetch values from table b only if a.state has a certain value. This is the hibernate mapping i had (used the example from the jBoss Documentation)
<discriminator column="state" type="string"/>
<subclass name="ClassB" discriminator-value="VALUE1">
<join table="b">
<key column="a_id"/>
<property name="amount" column="amount"/>
</join>
</subclass>
When i did this, my xml showed a syntax error stating that a hierarchy must be followed.
Is what I'm doing correct and if not, it would be great if someone could show me the way forward. Thanks.
P.S - more than one entry in table b will have the a_id column. However only one row in b will have the is_valid value set and its enough if i get this row in my POJO
It looks to me like you are mapping a table per subclass with discriminator strategy. This would imply a 1 - 1 row correlation between table a and table b, where the primary key of table b (the subclass) would also be a foreign key into table a.
However, your mapping is slightly odd in that you have
<key column="a_id" />
Typically this should be
<key column="id" />
And there would be no "a_id" column.
However, your db design looks like a one-to-many relationship rather than a subclass relationship.
Without your objects themselves, i can't really say what it is you're trying to do.
Take a look at the hibernate docs on inheritence.
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/inheritance.html

Hibernate - set of compound values

I need a Collection of compound values in Hibernate. Something like:
class Parent {
Set<Child> children;
}
class Child {
String property;
String anotherProperty;
MyOtherClass oneToOneClass;
}
The key requirement is that elements in this collection are value objects. When I saveOrUpdate the Parent it also saves its children.
More importantly when I create another Parent with children based on the same set, these children need to be persisted separately. That's why regular one-to-many does not work for me.
Is there a clean way I can do it with Hibernate? Something like collection of values described here: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/collections.html - but for a concrete, compound class.
I prefer solution in XML rather than annotations.
Normally you map this as composite-element
<set ... >
<key .../>
<composite-element class="Child" ...>
<property name="property"/>
<property name="anotherProperty"/>
<nested-composite-element name="oneToOneClass">
<property name="..."/>
</nested-composite-element>
</set>
See Component Mapping.

Hibernate Reference column in table to indicate how to unmarshall an attribute in a different column in the same table

I have an entity that I want to persist through Hibernate (3.2)
The EntityBean has a column that indicates how another value of the entity bean should be unmarshalled:
<class name="ServiceAttributeValue" table="service_attribute_value">
<cache usage="nonstrict-read-write"/>
<id name="id" column="id" type="int-long">
<generator class="native"/>
</id>
<property name="serviceAttribute" type="service-attribute" column="service_attribute" not-null="true" />
<!-- order is important here -->
<property name="value" type="attribute-value" not-null="true">
<column name="service_attribute" />
<column name="id_value"/>
<column name="enum_value"/>
<column name="string_value"/>
<column name="int_value"/>
<column name="boolean_value"/>
<column name="double_value"/>
</property>
</class>
The "service_attribute" column indicates which of the columns for the "value" property to look at when it unmarshalls the value and, more importantly, exactly what Type the value should be, for example the class of the Enum if the enum_value is to be read, or the type of Bean if the the id_value is to be read.
The value property uses a custom CompositeUserType to do the unmarshalling and within this I wish to reference the service_attribute column (although not write to it), however when I try to do this I get the following error:
org.hibernate.MappingException: Repeated column in mapping for entity: com.precurse.apps.rank.model.service.ServiceAttributeValue column: service_attribute (should be mapped with insert="false" update="false")
However within the definition of the composite property these xml attributes are not defined (only within a normal property).
Does anyone know of a way of overcoming this, or if there is a better solution to this propblem.
If you need any more information please let me know,
Cheers
Simon
I had a similar problem and changing the case of one column solved the problem. Could give a try!
e.g., one column could be service_attribute other Service_Attribute.
You can try this. Instead of mapping both values as property on the same table, map one of the property using join to itself and keep the other property as the way it is. This case you will be able to access the same property in both places. Just remember to name the property as different name.
<join table="service_attribute_value">
<key column = "id" />
<property name="serviceAttribute" type="service-attribute" column="service_attribute" not-null="true" />
</join>
<!-- order is important here -->
<property name="value" type="attribute-value" not-null="true">
<column name="service_attribute" />
<column name="id_value"/>
<column name="enum_value"/>
<column name="string_value"/>
<column name="int_value"/>
<column name="boolean_value"/>
<column name="double_value"/>
</property>
based on your description, it seems like what you want to do is creating different subclasses based on the service_attribute. Instead of trying to achieve repeated column mapping which is not allow in hibernate, you can take a look hibernate inheritance mapping.
I Think I found a solution albeit not a very elegant one.
in the
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
method of the CompositeUserType the "owner" argument passed to the method contains the id of the object who's service_attribute I want to access.
Annoyingly the actual serviceAttribute of the owner is not accessable or has not been set at this stage (I played around with the ordering of the elements in the hbm.xml config, in case this was an ordering thing, but unfortunatly still no joy), so I can't simply access it.
Anyway the id of the owner object is set, so I then used the session argument to run a HQL query based on the id to access the serviceAttribute which I then used to correctly unmarshall the value property.
The drawback of this solution is that it requires a HQL query as an overhead to the unmarshalling process, although its within the same session, its still not optimal.
If anyone has any ideas for a better solution I'd be very grateful.
Cheers

Hibernate many-to-many collection filtering

I have the following POJO with a Set inside:
class Word {
private Long id;
private String word;
private int type = WordListFactory.TYPE_DEFAULT;
private Set<Word> refs = new HashSet<Word>();
...
}
Here's the mapping XML:
<class name="kw.word.Word" table="word">
<id name="id" column="id" unsaved-value="null">
<generator class="native"/>
</id>
<property name="word"
unique="true"
not-null="true"/>
<property name="type"/>
<set name="refs"
table="word_key"
cascade="save-update">
<key column="word_id"/>
<many-to-many class="kw.word.Word"
column="word_ref_id"
fetch="join">
</many-to-many>
</set>
</class>
There are two tables: word and word_key. The latter links word-parents to word-children.
I'm trying to implement set items filtering when the data is fetched from DB. The resulting object set must contain only items with a specific type.
I tried various things:
Using filtering in mapping like (sorry for lack of brackets)
many-to-many class="kw.word.Word"
column="word_ref_id"
fetch="join">
filter name="word_type" condition="type=:type"
many-to-many
In the code that fetches data I enabled the filter and set the parameter. According to logs hibernate seems to ignore this particular filter as it there's no condition in resulting SQL query.
Using additional condition in Criteria
Word result = null;
session.beginTransaction();
Criteria crit = session.createCriteria(Word.class);
crit.add(Restrictions.like("word", key))
.createAlias("refs", "r")
.add(Restrictions.eq("r.type", getType()));//added alias and restriction for type
List list = crit.list();
if(!list.isEmpty())
result = list.get(0);
session.getTransaction().commit();
now the resulting SQL seems to be OK
select
this_.id as id0_1_,
this_.word as word0_1_,
this_.type as type0_1_,
refs3_.word_id as word1_,
r1_.id as word2_,
r1_.id as id0_0_,
r1_.word as word0_0_,
r1_.type as type0_0_
from
word this_
inner join
word_key refs3_
on this_.id=refs3_.word_id
inner join
word r1_
on refs3_.word_ref_id=r1_.id
where
this_.word like ?
and r1_.type=?
but right after this query there's another one that fetches all the items
select
refs0_.word_id as word1_1_,
refs0_.word_ref_id as word2_1_,
word1_.id as id0_0_,
word1_.word as word0_0_,
word1_.type as type0_0_
from
word_key refs0_
left outer join
word word1_
on refs0_.word_ref_id=word1_.id
where
refs0_.word_id=?
Maybe I'm doing something wrong?
From your given code snippet few points:
In case of many-to-many relationship you require 3 table , two entity tables and one join table. But as you are having same entity -Word , i think the given table structure and mappings seems fine.
Try to use HQL and specify 'LEFT JOIN FETCH' to specify which associations you need to be retrieved in the initial sql SELECT.
See this link related to many-to-many relationship,but they used criteria query.
Querying ManyToMany relationship with Hibernate Criteria

Categories

Resources