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.
Related
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.
For testing I'm using the H2 database.
For production it's MySQL.
I understrand that both support AUTO_INCREMENT (mysql / h2), but it seems like Hibernate doesn't work this way.
identity is supported for MySQL. Fine.
What about H2? Should I write my own generator or...? (using the org.hibernate.id.IdentifierGenerator interface as the doc says).
I must have a nice clean & quick way to get an ID (of type long by the way) from the database itself because the application is in a cluster (i.e several servers INSERT into the database at once)... that's why increment is definitely not for me.
Thanks!
You should just annotate your id property which needs the generated value with #GeneratedValue. This will automatically select the appropriate generation strategy for the database you're using. See GenerationType.AUTO for more details.
Your property will look like this:
#Id
#GeneratedValue
private long id;
Use the native generator, for example
<id name="id" type="int">
<column name="id_column" />
<generator class="native" >
<param name="sequence">id_column_sequence</param>
</generator>
</id>
The generator with the class native uses the best generation strategy for the database. In the case of MySql this is auto_increment, in the case of Oracle this is a sequence (and for H2 it also should be a sequence, but I've never tried, because I don't use H2). The generator parameter sequence only is used if it is useful, i. e. for MySql databases the parameter is ignored, and for Oracle it is used.
In that way you can use the same mapping file for different database types (at least as long as the table and column names are the same).
Say I have a table like so:
CREATE TABLE big_table (UUID varchar(32) not null, ... );
I have a query on the table that I can't express as an HQL or Criteria query. I am trying to set up the query as view in Oracle, like so:
CREATE VIEW big_table_view AS SELECT bt.* FROM big_table bt
LEFT OUTER JOIN ...
-- (multicolumn subselect over big_table for some historical stuff)
WHERE ...
I am trying to map the same Java class to both the table and the view. That would be really cool because then I can run the same Criteria queries against both, etc.
My problem is that I can't come up with an HBM mapping file that doesn't wreak havoc with my HSQLDB test code. My test setup is a typical Maven/Spring test setup with hibernate.hbm2ddl.auto set to create-drop so that Hibernate creates the HSQLDB schema on the fly for testing.
My mapping file currently looks like this:
<hibernate-mapping>
<class name="com.example.BigPojo" entity-name="bigPojo"
table="big_table">
&commonPropertiesEntity;
</class>
<class name="com.example.BigPojo" entity-name="bigPojoView"
table="big_table_view">
&commonPropertiesEntity;
</class>
</hibernate-mapping>
...when I run my tests, they blow up all over the place because HSLQDB is trying to create a table called big_table_view with all the same foreign key constraints, indices, etc. I'm trying to fix the schema after it gets created via a database-object element like this:
<database-object>
<create>
DROP TABLE big_table_view CASCADE;
CREATE VIEW big_table_view...
</create>
<drop>
DROP VIEW big_table_view IF EXISTS;
</drop>
<dialect-scope name="org.hibernate.dialect.HSQLDialect" />
</database-object>
...but there's still something breaking and I'm still wading through trying to figure it out. Is there a way to tell Hibernate to exclude the bigPojoView entity from hbm2ddl? Is there a better way to do this mapping generally? I'm open to any advice...
There's no easy way to exclude a table from hbm2ddl. You can, however, map your view via Hibernate's subselect: see footnote #20 under 5.1.1.
Something like:
<class name="com.example.BigPojo" entity-name="bigPojoView"
<subselect>
... your view definition here ...
</subselect>
<synchronize table="big_table"/>
<id name="UUID"/>
...
</class>
I have a hibernate mapping:
<properties name="lrt_bps_bpr_acs_uk" unique="true">
<many-to-one name="activitySummary" column="bps_acs_id" class="xyz.ActivitySummary"
unique-key="lrt_bps_bpr_acs_uk"/>
<many-to-one name="bestPractice" column="bps_bpr_id" class="xyz.BestPractice"
unique-key="lrt_bps_bpr_acs_uk"/>
</properties>
I expected that hibernate would create unique constraint on database for this mapping. Nothing like that happened however.
What am I doing wrong?
Seems like you do nothing wrong. We do it the same way in our project and it works fine.
I thought to have the same problem once because I couldn't find the constaints in our oracle DB because I thought the specified unique-key attribute value (in your case 'lrt_bps_bpr_acs_uk') is used as constraint_name in the DB. But that isn't the case (at least with oracle DB).
What's the actual DDL that Hibernate is generating? What would be the DDL if you remove the unique-key parameters? What's the Hibernate version?
From the documentation, it seems you are doing it correctly:
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html#mapping-declaration-properties
Dont specify name="lrt_bps_bpr_acs_uk" unique="true" in the properties element. It automatically takes it and applies the constraint with unique-key
I am trying to audit the action that the user performed that resulted in changes to corresponding tables. For example if a user were to transfer money between 2 accounts this would generate the following sequence of events:
Insert transfer amount into Transfer table
Subtract transfer amount from balance in Balance Table for Account 1.
Add transfer amount to balance in Balance Table for Account 2.
The parent audit message for all tables would be: "User generated transfer for amount XXX"
This is achieved with the following schema:
schema
alt text http://img48.imageshack.us/img48/7460/auditloggingiv6.png
The question is how do I represent this in hibernate?
I have created the following:
In Balance and Transfer's mapping files
<set name="auditRecords" table="TransferAuditRecord" inverse="false" cascade="save-update">
<key>
<column name="AuditRecordID" not-null="true" />
</key>
<one-to-many class="audit.AuditRecord"/>
</set>
Transfer and Balance classes then implement IAuditable which has methods
public void setAuditRecords(Set<AuditRecord> auditRecord);
public Set<AuditRecord> getAuditRecords();
In AuditRecord's mapping file I have:
<many-to-one name="parentAuditRecord" lazy="false"
column="parent_id"
class="audit.AuditRecord"
cascade="all" />
Then in Logging class using AOP and Hibernate Interceptors I have:
AuditRecord auditRecord = new AuditRecord();
auditRecord.setUser(userDAO.findById(
org.springframework.security.context.SecurityContextHolder.getContext()
.getAuthentication().getName()));
auditRecord.setParentAuditRecord(getCurrentActiveServiceRecord());
auditable.getAuditRecords().add(auditRecord);
Then in the Service Class I call the following method, enclosed in a transaction:
save(balance1);
save(balance2);
transfer.setPassed(true);
update(transfer);
The parentAuditRecord is created using AOP with a thread safe stack, and the AuditRecordType_id is set using annotations on the method.
I left out the "passed" column on the transfer table. Previously I call save(transfer) to insert the transfer amount into the Transfer table with passed set to false. (This action is also audited).
My requirements are slightly more complicated than the example above :P
So the sequence of events for the above should be:
Update Transfer Table
Insert into AuditRecord (Parent)
Insert into AuditRecord (Child)
Insert into TransferAuditRecord
Insert into Balance Table
Insert into AuditRecord (Child)
Insert into BalanceAuditRecord
Insert into Balance Table
Insert into AuditRecord (Child)
Insert into BalanceAuditRecord
However the cascade options defined above fail at the update statement. Hibernate refuses to insert a record into the many-to-many table (even if unsaved-value="any" on the AuditRecord Mapping). I always want to insert rows into the many-to-many tables so potentially one Transfer has many Audit Records marking the previous events. However, the latest event determines the message the user wants to see. Hibernate either tries to update the many-to-many table and previous AuditRecord entries or it simply refuses to insert into AuditRecord and TransferAuditRecord, throwing a TransientObjectException.
The Audit Message is retrieved something like this:
msg=... + ((AuditRecord) balance.getAuditRecords().toArray()[getAuditRecords().size()-1])
.getParentAuditRecord().getAuditRecordType().getDescription() + ...;
The message should say something like this:
"Username set transfer to passed at 12:00 11-Oct-2008"
EDIT I decided to go with explicitly mapping the many-to-many table (with an associated interface), and then in afterTransactionCompletion, calling save on the parent audit record (which cascades the save to the child audit records) then explicitly saving the interface on all child mapping tables. This isn't a true audit history, rather a non-invasive method of recording user action. I will look into Envers if I need more complete audit history at a later point.
Seems like the relationship between parentAuditRecord and transferauditrecord and balance auditrecord shouldn't be one to many. When I read what you typed I'm seeing it as a table per subclass usage of that audit hierarchy which is a one-to-one relationship.
http://www.hibernate.org/hib_docs/reference/en/html/inheritance.html
You may also want to check out JBoss's Envers project.
At the design level, it seems like a insert only db design would work marvels here.
If you want to keep it the way it is right now (which I'm sure you do), you could look into Hibernate listeners/interceptors/events (well defined in the doc: http://www.hibernate.org/hib_docs/v3/reference/en-US/html_single/)
Else, I just looked into JBoss Envers and it also seems pretty useful.