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
Related
Consider three tables. Interest date can come from Master document or Manual Override. If it from Master Document, the master_doc_id carries the reference and if it comes from manual override manual_override_id carries the reference
INTEREST_RATE
id,
rate,
master_doc_id,
manual_override_id
MASTER_DOC
id,
<Master doc related fields>
MANUAL_OVERRIDE
id,
<Manual Overide related fields>
Interest rate comes from either Master doc or manual override. Meaning, either one can have an id, and the other null. Never will both have values, or will both be null
Both MasterDoc and ManualOverride provide one-to-many mapping to Interest
MasterDoc
<set access="field" name="interestRates" inverse="false" cascade="all-delete-orphan">
<cache usage="read-write"/>
<key column="MASTER_DOC_ID"/>
<one-to-many class="com.foo.foos.InterestRate"/>
</set>
ManualOverRide
<set access="field" name="interestRates" inverse="false" cascade="all-delete-orphan">
<cache usage="read-write"/>
<key column="MANUAL_OVERRIDE_ID"/>
<one-to-many class="com.foo.foos.InterestRate"/>
</set>
This works fine.
But, i need to enforce the contstraint that, either of them has to have a value and the other null.
I tried.
Obvious not-null in one-to-many mapping. Obvious that this will fail because, both columns cant have values together.
Constraint on the table, (in the database). But, hibernate creates an insert followed by an update on the table. Even though the final result would be valid, the constraint will fail after the initial insert.
ex:
while uploading the initial file
insert into master_doc......
insert into interest_rate.... (with null values in master_doc_id and manual_override_id)
update interest_rate (set master_doc_id = id of master_doc)
The constraint fails on line no 2.
Any way I can get this to work. I need to have this constraint in place, to prevent manual updates on the table.
I'm using Postgres 9.3.5 and recently updated the hibernate from 3.2 to 4.3.11.
As a result I can't run "SELECT... FOR UPDATE OF" queries,
and simply 'select.. for update' is not enough in my case since it returns
could not extract ResultSet. Reason: ERROR: FOR UPDATE cannot be applied to the nullable side of an outer join
The criteria I'm trying to use looks like this:
Criteria criteria = session.createCriteria(objectType).add(Restrictions.eq("name", objectName).ignoreCase());
I'm using the following locking:
in 3.2: criteria.setLockMode(LockMode.UPGRADE);
in 4.3.11: criteria.setLockMode(LockMode.PESSIMISTIC_WRITE);
I have an hierarchy of hibernate (& DB) objects which cause the hibernate perform several joins while constructing the above query.
the 'objectType' is a joined-subclass of the main class
<class name="BaseObject" table="BASE_OBJECTS">
While using hibernate 3.2 the final query (taken from Postgres logs) ended with: "for update of this_2_"
(when this_2_ is the alias given by hibernate to the main table (BaseObject) mapped in hbm.xml file)
After upgrading to 4.3.1.1 the same query returns the above mentioned exception.
which means the final query performed as for update (without the name of the table on which to perform the lock)
After an extensive look of the web I could find only that the "for update of" in hibernate with Postgres is not supported any more?
[https://hibernate.atlassian.net/browse/HHH-5654][2]
It seems very unlikely since it's quite an important sql feature and a big degradation in usage.
Am I missing something here?
02.09.15:
I'll try to clarify myself:
using an example given in the hibernate documentation
at
https://docs.jboss.org/hibernate/orm/3.5/reference/en/html/inheritance.html
class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="creditCardType" column="CCTYPE"/>
...
</joined-subclass>
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
If I want to perform something like:
select p from Payment p where id=1
Hibernate will perform an outer join (on the key) on all tables .
Adding a lock (.setLockMode(LockMode.PESSIMISTIC_WRITE)) will lock the lines on the four tables (as 'For update'),
instead of only on table "Payments" ('for update of p') - which did happen in hibernate 3.2
So what We have, is that Something which was supplied earlier by hibernate, is not working any more, using their own mapping examples?
Thanks in advance
Marina
The issue was fixed in Hibernate 5.
Tested in 5.2.8.Final.
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.
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
I am having a querying issue in Hibernate. I have a table, 'test', with existing data. I have a requirement where I can not modify the schema of the test table, so I created another table, 'testExtension', whose primary key is a foreign key to the primary key of Test. Data in testExtension is a subset of the data in test. i.e. There will be less rows in 'testExtension' than in 'test'.
I have defined this relationship in a configuration file as follows:
<class name="Test" table="test">
<id name="testId" column="test_id">
<generator class="sequence">
<param name="sequence">test_id_seq</param>
</generator>
</id>
<property name="name"/>
<joined-subclass name="TestExtension" table="testExtension">
<key column="test_id"/>
<property name="summary" />
<property name="homepage"/>
</joined-subclass>
With this setup, I am able to create a TestExtension object in my Java program, populate it with data, 'save' it via Hibernate, and commit the transaction. And it correctly saves data in both Test and TestExtension.
My problem is occurring when I am trying to query data from these tables. Right now if I query for a particular test_id using the TestExtension.class to QBE, it will only return a row if that id exists in both Test and TestExtension. If I use the Test.class to QBE, it will return the row but I will not have access to any of the data stored in TestExtension.
My question is: how can I query these tables so that the results are based off a 'left outer join' of both Test and TestExtension? Any solution is appreciated, whether it's query by example, HQL, or something else (though preferably not raw SQL).
Thanks!
HQL is probably the easiest way to do this. Docs are here:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/queryhql-joins.html
Sounds like what you might want to do is remap your relationships so that Test and TestExtension use a one-to-one relationship instead of inheritance. Then you can query for Test and TestExtension using a left outer join across the one-to-one.
If you use HQL to write a query for the Test class, it should do what you want. I assume QBE is effectively adding the class of your example entity as one of the query parameters.
So sth like:
from Test t where t.property = :value
should return either Test or TestExtension entities. Note that (at least with the versions of Hibernate I've used). In this case, Hibernate should immediately give you back the actual entities rather than a proxy too--- be aware that TestExtension entities can sometimes be returned as plain Test lazy-loading proxies.