These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: Class ICriteria.List() SQL has subclass properties in it
PostPosted: Sat Aug 19, 2006 12:39 pm 
Newbie

Joined: Mon Aug 07, 2006 6:40 pm
Posts: 12
Location: Atlanta, GA
I have a class with a subclass. When I use an ICriteria.List() call with expressions for the class, it creates a select clause with the columns of the sub-class. I have outer-join turned off in the NHibernate configuration section. When I use the Load() call with the primary key (composite), it works. The expression List() call fails with either a key on non-key property.

Hibernate version:
Using 1.2.0.Alpha1

Mapping documents:
Parent Class
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class name="PRG.CPFS.Objects.PackageTask,PRG.CPFS.Objects" table="package_task" lazy="true" dynamic-update="true" dynamic-insert="true" polymorphism="explicit">

<composite-id name="PackageTaskKey">
<key-property name="Package" column="[package]" type="Int32" />
<key-property name="Task" column="[task]" type="String"/>

</composite-id>
<discriminator />

<property column="[task_status]" type="String" name="TaskStatus" not-null="true" length="10" />
<property column="[created]" type="DateTime" name="Created" update="false" insert="false" not-null="true" />
<property column="[created_by]" type="String" name="CreatedBy" update="false" insert="false" not-null="true" length="256" />
<property column="[updated]" type="DateTime" name="Updated" update="false" insert="false" not-null="true" />
<property column="[updated_by]" type="String" name="UpdatedBy" update="false" insert="false" not-null="true" length="256" />
<property column="[sequence]" type="Int32" name="Sequence" not-null="true" />
<property column="[retry_count]" type="Int32" name="RetryCount" not-null="true" />
<property column="[subsequence]" type="Int32" name="Subsequence" />
<property column="[auto_ready]" type="Boolean" name="AutoReady" not-null="true" />
<property column="[emergency_hold]" type="Boolean" name="EmergencyHold" />
<property column="[initiated]" type="DateTime" name="Initiated" />
<property column="[initiated_by]" type="String" name="InitiatedBy" length="256" />


<many-to-one name="TaskStatusObject" class="PRG.CPFS.Objects.TaskStatus, PRG.CPFS.Objects" cascade="all">
<column name="[task_status]" />
</many-to-one>
<many-to-one name="TaskObject" class="PRG.CPFS.Objects.Task, PRG.CPFS.Objects" cascade="all">
<column name="[task]" />
</many-to-one>
<many-to-one name="PackageObject" class="PRG.CPFS.Objects.Package, PRG.CPFS.Objects" cascade="all">
<column name="[package]" />
</many-to-one>
</class>
</hibernate-mapping>

Sub-Class using table-per-class hierarchy strategy
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<subclass extends="PRG.CPFS.Objects.PackageTask,PRG.CPFS.Objects" name="PRG.CPFS.Objects.PackageTaskEx,PRG.CPFS.Objects" lazy="true" dynamic-update="true" dynamic-insert="true">
<property column="[status_class]" type="String" name="StatusClass" length="10" update="false" insert="false" />
<property column="[user_intervention]" type="Boolean" name="UserIntervention" update="false" insert="false" />
<property column="[external_system_intervention]" type="Boolean" name="ExternalSystemIntervention" update="false" insert="false" />
<property column="[reversal_process_required]" type="Boolean" name="ReversalProcessRequired" />
</subclass>
<sql-query name="ActivePackageTasks">
<return alias="PackageTaskEx" class="PRG.CPFS.Objects.PackageTaskEx,PRG.CPFS.Objects"/>
SELECT {PackageTaskEx.*} FROM [dbo].[vw_active_package_tasks] {PackageTaskEx} ORDER BY [package], [sequence], [subsequence]
</sql-query>
<sql-query name="ActivePackagesForTasks">
<return alias="PackageTaskEx" class="PRG.CPFS.Objects.PackageTaskEx,PRG.CPFS.Objects"/>
SELECT {PackageTaskEx.*} FROM [dbo].[vw_active_package_tasks] {PackageTaskEx}
WHERE [task] IN ( :task_list ) ORDER BY [package], [sequence], [subsequence]
</sql-query>
<sql-query name="ActivePackagesForTasksByStatus">
<return alias="PackageTaskEx" class="PRG.CPFS.Objects.PackageTaskEx,PRG.CPFS.Objects"/>
SELECT {PackageTaskEx.*} FROM [dbo].[vw_active_package_tasks] {PackageTaskEx}
WHERE [task] IN ( :task_list ) AND [task_status] IN ( :task_status_list ) ORDER BY [package], [sequence], [subsequence]
</sql-query>
<sql-query name="ActivePackagesByTaskStatus">
<return alias="PackageTaskEx" class="PRG.CPFS.Objects.PackageTaskEx,PRG.CPFS.Objects"/>
SELECT {PackageTaskEx.*} FROM [dbo].[vw_active_package_tasks] {PackageTaskEx}
WHERE [task_status] IN ( :task_status_list ) ORDER BY [package], [sequence], [subsequence]
</sql-query>
<sql-query name="ActiveTasksForPackages">
<return alias="PackageTaskEx" class="PRG.CPFS.Objects.PackageTaskEx,PRG.CPFS.Objects"/>
SELECT {PackageTaskEx.*} FROM [dbo].[vw_active_package_tasks] {PackageTaskEx}
WHERE [package] IN ( :package_list ) ORDER BY [package], [sequence], [subsequence]
</sql-query>
<sql-query name="ActiveTasksForChildPackages">
<return alias="PackageTaskEx" class="PRG.CPFS.Objects.PackageTaskEx,PRG.CPFS.Objects"/>
SELECT {PackageTaskEx.*} FROM [dbo].[vw_active_child_package_tasks] {PackageTaskEx}
WHERE [package_type] IN ( :child_package_type ) AND [parent_package] IN ( :package_list )
ORDER BY [package], [sequence], [subsequence]
</sql-query>
</hibernate-mapping>

Code between sessionFactory.openSession() and session.close():
ICriteria de = Se.CreateCriteria( typeof( PackageTask ));

de.Add( new NHibernate.Expression.EqExpression( "PackageTaskKey.Package", 7900 ) );

IList<PackageTask> ws = de.List<PackageTask>();

Full stack trace of any exception that occurs:
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters) in c:\net\nhibernate\nhibernate\src\NHibernate\Loader\Loader.cs:line 1646
at NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor session, QueryParameters queryParameters) in c:\net\nhibernate\nhibernate\src\NHibernate\Loader\Loader.cs:line 1593
at NHibernate.Loader.Loader.List(ISessionImplementor session, QueryParameters queryParameters, ISet querySpaces, IType[] resultTypes) in c:\net\nhibernate\nhibernate\src\NHibernate\Loader\Loader.cs:line 1587
at NHibernate.Loader.Criteria.CriteriaLoader.List(ISessionImplementor session) in c:\net\nhibernate\nhibernate\src\NHibernate\Loader\Criteria\CriteriaLoader.cs:line 65
at NHibernate.Impl.SessionImpl.Find(CriteriaImpl criteria, IList results) in c:\net\nhibernate\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 4907
at NHibernate.Impl.SessionImpl.Find[T](CriteriaImpl criteria) in c:\net\nhibernate\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 4856
at NHibernate.Impl.CriteriaImpl.List[T]() in c:\net\nhibernate\nhibernate\src\NHibernate\Impl\CriteriaImpl.cs:line 285
at ConsoleApplication1.TestHarness.Main(String[] args) in C:\Documents and Settings\bsayles\My Documents\Visual Studio 005\Projects\CPFS\TestHarness\TestHarness.cs:line 109

Inner Stack Trace
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader()
at NHibernate.Impl.BatcherImpl.ExecuteReader(IDbCommand cmd) in c:\net\nhibernate\nhibernate\src\NHibernate\Impl\BatcherImpl.cs:line 224
at NHibernate.Loader.Loader.GetResultSet(IDbCommand st, RowSelection selection, ISessionImplementor session) in c:\net\nhibernate\nhibernate\src\NHibernate\Loader\Loader.cs:line 1289
at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) in c:\net\nhibernate\nhibernate\src\NHibernate\Loader\Loader.cs:line 393
at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) in c:\net\nhibernate\nhibernate\src\NHibernate\Loader\Loader.cs:line 182
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters) in c:\net\nhibernate\nhibernate\src\NHibernate\Loader\Loader.cs:line 1636
Name and version of the database you are using:
MS SQL2000 SP3

The generated SQL (show_sql=true):
SELECT this_.[package] as column1_0_, this_.[task] as column2_0_, th
is_.[task] as column2_26_0_, this_.[sequence] as column9_26_0_, this_.[subsequen
ce] as column11_26_0_, this_.[package] as column1_26_0_, this_.[initiated] as co
lumn14_26_0_, this_.[initiated_by] as column15_26_0_, this_.[created] as column5
_26_0_, this_.[created_by] as column6_26_0_, this_.[updated_by] as column8_26_0_
, this_.[auto_ready] as column12_26_0_, this_.[emergency_hold] as column13_26_0_
, this_.[task_status] as column4_26_0_, this_.[retry_count] as column10_26_0_, t
his_.[updated] as column7_26_0_, this_.[status_class] as column16_26_0_, this_.[
user_intervention] as column17_26_0_, this_.[external_system_intervention] as co
lumn18_26_0_, this_.[reversal_process_required] as column19_26_0_, this_.class a
s class0_ FROM package_task this_ WHERE this_.[package] = @p0
@p0 = '7900'

Debug level Hibernate log excerpt:
Identical to stack trace in exception

NHibernate.ADOException text
could not execute query

SQLException Message text
Invalid column name 'status_class'.
Invalid column name 'user_intervention'.
Invalid column name 'external_system_intervention'.
Invalid column name 'reversal_process_required'.
Invalid column name 'class'.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Aug 20, 2006 9:14 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
That's one very odd mapping. You're trying to use the table per class hierarchy inheritance strategy, but you've deliberately bypassed the discriminator? How will that work? And why would adding in the subclass' columns into a proper table per class hierarchy affect anything, seeing as every row is in the same table and thus has the same columns? The mapped entities won't have the properties, but the columns will still exist, so the query will work.

Unfortunately you haven't posted the actual SQL error, just the SqlClient exception telling you that there has been an SQL error. If fixing the discriminator doesn't help, post the actual exception.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Aug 20, 2006 10:54 pm 
Newbie

Joined: Mon Aug 07, 2006 6:40 pm
Posts: 12
Location: Atlanta, GA
In regards to the error text, that is the error from the SQL. I cut the SQL statement out of the log and executed it interactively and the following error messages were generated. The only differences are the line #/Msg # indicators:

Msg 207, Level 16, State 3, Line 1
Invalid column name 'status_class'.
Msg 207, Level 16, State 3, Line 1
Invalid column name 'user_intervention'.
Msg 207, Level 16, State 3, Line 1
Invalid column name 'external_system_intervention'.
Msg 207, Level 16, State 3, Line 1
Invalid column name 'reversal_process_required'.
Msg 207, Level 16, State 3, Line 1
Invalid column name 'class'.

This error is correct, because the table named in the query, package_task, does not contain these columns. These columns are provided by a named query that returns these additional columns into objects of the sub-class PackageTaskEx from views such as vw_active_package_tasks, not its parent class PackageTask which references the table package_task directly. However, the sub-class correctly updates the table package_task since that is what I want, the
Code:
dynamic-update="true"
ensures that.

The subclasses provide a mechanism to obtain additional data to decide what to do with a given instance of what would be the parent class. There is no discriminating value. I would like to take the default discriminator value of the class name itself, which in fact is how the sub class behaves. However, the sub-class mapping is not valid without a <discriminator> tag in the parent class even though it has no purpose.

It is possible that this empty discriminator, which allows the named-query and the rest of the strategy to work, is having a side-effect of adding the sub-class properties. But the documentation seems to indicate that the
Code:
polymorphism="explicit"
should prevent the engine from trying to get data for subclasses at the same time.

Perhaps I could use the joined-subclass instead even though I am trying for the table-per-class hierarchy strategy.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Aug 20, 2006 11:39 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
The discriminator does not serve no purpose in this case. The only problem is that you want it to serve no purpose.

You have told hibernate that your virtual columns (status_class, etc.) are real columns, and hibernate knows 1) that it cannot load PackageTaskExs without those columns, and 2) that the package_task table has these columns. It knows these facts because your mapping says so. However, the columns don't exist, hence the problem. To fix this, you must either add the columns and use them properly, or else change your mapping to tell hibernate that they are not real columns.

The quick fix is to use formula="" instead of column="" for all the derived columns. This has the bonus that you can remove all the insert="false" update="false" bits, because hibernate knows that derived columns are not real columns and thus cannot be inserted or updated.

You may find that you don't need the views like vw_active_child_package_tasks, and that you can work the entire thing out using the formula tag.

However, this is not the "correct" solution. Here are two solutions that are (IMO, of course) more "correct".
  1. Accept that PackageTaskEx is not a "kind of" PackageTask, it is a way of looking at a PackageTask, and thus not suitable for polymorphism. Don't use explicit polymorphism, use implicit: map PackageTaskEx in its entirety (without the subclass bit), and add mutable="false" for optimization.
  2. Eliminate PackageTaskEx entirely, and instead have an associated object (via one-to-one) from PackageTask that includes the Ex columns. Mark it mutable="false" lazy="true" and use the appropriate view to load it. This is also the most object-oriented solution, I think, as the "real" PackageTask is the only PackageTask object needed, and the assoicated object is a PackageTaskExtendedProperties object (or whatever) that doesn't contain any of PackageTask's properties.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Aug 20, 2006 11:57 pm 
Newbie

Joined: Mon Aug 07, 2006 6:40 pm
Posts: 12
Location: Atlanta, GA
I was afraid of that, but I was relying on the discriminator value as class name behavior to act as if there is no discriminator.

In any event, I had previously considered using formula="" but was not sure that Hibenate would allow the proxy to load it with the values not derived from columns identifiable as formulas. I am not sure why I didn't try it frist. I will do so, and that should fix it; and Thanks.

I am using a customized MyGeneration template to generate all the mapping and DAL files for the physical table objects in the database, and using hand-written (for lack of a better term) mappings and DAL for collections and many-to-one/one-to-many relationships not present in the FK definitions. That is the true reason for the xxxEX classes.

This system is a (somewhat) generic task processor engine for auditing processes. The profiles for the packages are complex, and controls around what tasks are "active" for each package or a related "child" package are as well. There are obviously many ways to skin a cat, but sometimes I find it easier to bury complex queries in views that are referenced in many places. I am not yet as versed in HQL and ORM-oriented thinking to give up on SQL yet. I don't think I can pull off using formulas to replace them.

What I have done so far, I am sold on hibernate and will continue to exploit features as this system develops.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 21, 2006 12:39 am 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
You might find a way to make use of hibernate's lazy loading by taking some of idea #2 on board. Adding a PackageTaskEx associated immutable entity is better than creating a whole new fake object containing 95% of the same information as in PackageTask. Also there's the OO point: A PackageTaskEx as you've defined it is not a "kind-of" PackageTask, and thus should not extend PackageTask. It's just an extra add-on that contains a few read-only fields relevant to PackageTask.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.