I am getting absurd proxy usage from reasons I can't begin to fathom.
I apologize that I have not posted a "main method" to JIRA as I simply haven't had time because I have been trying to get my project done any way I can first but I keep getting stuck on the weird problems with the results I am getting from Hibernate (I guess this is a chicken and egg type problem, do I keep pounding away at the problem to try to get my project working or do I let the project slide while I figure out how to post to JIRA which might result in a solution to my problems which would get the project working).
Loading the same persisted class hierarchy, just with different values (different records), using the same method, I am getting varying results as to whether or not the objects are "initialized" or just proxies which will generate the infamous LazyInitializationException when I try to use them later in a View (MVC).
I have the following mappings (unabridged):
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<!-- WorksheetProduct -->
<class name="com.fgl.ina.costestimation.WorksheetProduct" table="worksheet_product">
<id name="ID" type="integer" column="worksheet_product_id" unsaved-value="0">
<generator class="net.sf.hibernate.id.IdentityGenerator"/>
</id>
<many-to-one name="parentWorksheet" column="worksheet_id" not-null="true"/>
<property name="productID" column="ina_prod_id" type="long" not-null="false"/>
<property name="unitOfMeasureQuantity" column="uom_qty" type="float" not-null="false"/>
<bag name="costFactors" table="worksheet_factor" lazy="false" inverse="true" cascade="all-delete-orphan" order-by="worksheet_factor_id" outer-join="true">
<key column="worksheet_product_id"/>
<!-- <index column="worksheet_factor_id" type="string"/>-->
<one-to-many class="com.fgl.ina.costestimation.WorksheetFactor"/>
</bag>
</class>
<!-- WorksheetHeader -->
<class name="com.fgl.ina.costestimation.WorksheetHeader" table="worksheet_header">
<id name="worksheetID" type="integer" column="worksheet_id" unsaved-value="0">
<generator class="net.sf.hibernate.id.IdentityGenerator"/>
</id>
<property name="description" column="description" type="string"/>
<property name="vendor" column="vendor_no" type="string" not-null="true"/>
<property name="status" column="status" type="short" not-null="true"/>
<!-- <many-to-one name="status" class="com.fgl.ina.costestimation.lookups.WorksheetStatus"-->
<!-- column="status" update="false" insert="false" cascade="none" outer-join="true"/>-->
<property name="createdBy" column="created_by" type="string" not-null="true"/>
<property name="createdDate" column="created_date" type="timestamp" not-null="true"/>
<property name="revisedDate" column="last_revision_date" type="timestamp" not-null="true"/>
<property name="foreignStatus" column="foreign_status" type="short" not-null="true"/>
<!-- <bag name="validForeignStatuses" lazy="false" cascade="none" outer-join="true" order-by="foreign_status">-->
<!-- <many-to-many class="com.fgl.ina.costestimation.lookups.ForeignExchangeStatus" outer-join="true"/>-->
<!-- </bag> -->
<property name="foreignContractNo" column="foreign_contract_no" type="long" not-null="true"/>
<property name="comments" column="comments" type="string"/>
<map name="products" table="worksheet_product" lazy="true" inverse="true" cascade="all" order-by="worksheet_product_id" outer-join="true">
<key column="worksheet_id"/>
<index column="worksheet_product_id" type="string"/>
<one-to-many class="com.fgl.ina.costestimation.WorksheetProduct"/>
</map>
</class>
<!-- WorksheetFactor -->
<class name="com.fgl.ina.costestimation.WorksheetFactor" table="worksheet_factor">
<id name="ID" type="integer" column="worksheet_factor_id" unsaved-value="0">
<generator class="net.sf.hibernate.id.IdentityGenerator"/>
</id>
<many-to-one name="parentProduct" column="worksheet_product_id" not-null="true"/>
<property name="costFactorID" column="cost_factor_id" type="integer"/>
<property name="calculatedType" column="calculated_type" type="short"/>
<property name="costFactorValue" column="cost_factor_value" type="float"/>
<property name="foreignAmount" column="foreign_amount" type="float"/>
<property name="currencyType" column="currency_type" type="short"/>
<property name="exchangeRate" column="exchange_rate" type="float"/>
<property name="proratingType" column="prorating_type" type="short"/>
<many-to-one name="costFactor" class="com.fgl.ina.costestimation.CostFactor" insert="false" update="false"
cascade="none" column="cost_factor_id" outer-join="true"/>
</class>
<!-- Cost Factor Description -->
<class name="com.fgl.ina.costestimation.CostFactorDescription" table="cost_factor_description" mutable="false">
<id name="costFactorID" type="integer" column="cost_factor_id">
<generator class="net.sf.hibernate.id.IdentityGenerator"/>
</id>
<property name="language" column="locale" type="string"/>
<property name="description" column="description" type="string"/>
</class>
<!-- Cost Factor -->
<class name="com.fgl.ina.costestimation.CostFactor" table="cost_factor" mutable="false">
<id name="costFactorID" type="integer" column="cost_factor_id">
<generator class="net.sf.hibernate.id.IdentityGenerator"/>
</id>
<set name="calculatedTypeOptions" lazy="false" cascade="none" where="locale='en'" outer-join="true">
<key column="cost_factor_id"/>
<!-- <index column="allowable_type" type="integer"/>-->
<one-to-many class="com.fgl.ina.costestimation.lookups.CalculatedTypeLookup"/>
</set>
<set name="currencyTypeOptions" lazy="false" cascade="none" outer-join="true">
<key column="cost_factor_id"/>
<!-- <index column="allowable_type" type="integer"/>-->
<one-to-many class="com.fgl.ina.costestimation.lookups.CurrencyTypeLookup"/>
</set>
<set name="proratingTypeOptions" lazy="false" cascade="none" where="locale='en'" outer-join="true">
<key column="cost_factor_id"/>
<!-- <index column="allowable_type" type="integer"/>-->
<one-to-many class="com.fgl.ina.costestimation.lookups.ProratingTypeLookup"/>
</set>
<bag name="descriptions" lazy="false" cascade="none" table="cost_factor_descriptions" where="locale='en'" outer-join="true">
<key column="cost_factor_id"/>
<!-- <index column="locale" type="string"/>-->
<one-to-many class="com.fgl.ina.costestimation.CostFactorDescription"/>
</bag>
</class>
</hibernate-mapping>
I have recently made one mapping have lazy=true but that hasn't changed the strange results I get (if anything it has possibly made things worse?).
It seems that if I have more than one record for the first level Map of WorksheetProduct that I can be guaranteed that the various Sets and Bags further down the hierarchy will be proxies. However, if I only have one record for WorksheetProduct everything in the hierarchy loads correctly (which it didn't used to do when the Map of WorksheetProducts was set as lazy=false, then I used to get a proxy for that Map anyway, and also for the Bag/Map I have on it, and then only on the Bag of descriptions further down the hierarchy but consistently all of the time for any record even though they were all set as lazy=false).
I have tried all kinds of clueless hacking about with configuration properties such as batch_size, max_fetch_depth, use_outer_join, etc. that have not affect things that I can tell.
Here is the current incarnation of my hibernate.properties file:
Code:
######################
### Query Language ###
######################
## define query language constants / function names
hibernate.query.substitutions true 1, false 0, yes 'Y', no 'N'
## package imports
hibernate.query.imports net.sf.hibernate.test, net.sf.hibernate.eg
#################
### Platforms ###
#################
## JNDI Datasource
hibernate.connection.datasource ina
hibernate.connection.username <deleted>
hibernate.connection.password <deleted>
## MS SQL Server
hibernate.dialect net.sf.hibernate.dialect.SybaseDialect
#################################
### Hibernate Connection Pool ###
#################################
hibernate.connection.pool_size 0
hibernate.statement_cache.size 25
###########################
### C3P0 Connection Pool###
###########################
#hibernate.c3p0.max_size 2
#hibernate.c3p0.min_size 2
#hibernate.c3p0.timeout 5000
#hibernate.c3p0.max_statements 100
#hibernate.c3p0.validate false
###################################
### Apache DBCP Connection Pool ###
###################################
## connection pool
#hibernate.dbcp.maxActive 100
#hibernate.dbcp.whenExhaustedAction 1
#hibernate.dbcp.maxWait 120000
#hibernate.dbcp.maxIdle 10
## prepared statement cache
#hibernate.dbcp.ps.maxActive 100
#hibernate.dbcp.ps.whenExhaustedAction 1
#hibernate.dbcp.ps.maxWait 120000
#hibernate.dbcp.ps.maxIdle 100
## optional query to validate pooled connections:
#hibernate.dbcp.validationQuery select 1 from dual
#hibernate.dbcp.testOnBorrow true
#hibernate.dbcp.testOnReturn false
##############################
### Proxool Connection Pool###
##############################
## Properties for external configuration of Proxool
#hibernate.proxool.pool_alias pool1
## Only need one of the following
#hibernate.proxool.existing_pool true
#hibernate.proxool.xml proxool.xml
#hibernate.proxool.properties proxool.properties
## Or, alternatively, all of these
## Standard configuration properties of Proxool
#hibernate.proxool.house-keeping-sleep-time 30000
#hibernate.proxool.house-keeping-test-sql
#hibernate.proxool.maximum-connection-count 4
#hibernate.proxool.maximum-connection-lifetime 4
#hibernate.proxool.simultaneous-build-throttle 2
#hibernate.proxool.maximum-active-time 500
#hibernate.proxool.minimum-connection-count 2
#hibernate.proxool.fatal-sql-exception
#hibernate.proxool.prototype-count
#hibernate.proxool.statistics
#hibernate.proxool.recently-started-threshold
#hibernate.proxool.overload-without-refusal-lifetime
#################################
### Plugin ConnectionProvider ###
#################################
## use a custom ConnectionProvider (if not set, Hibernate will choose a built-in ConnectionProvider using hueristics)
#hibernate.connection.provider_class net.sf.hibernate.connection.DriverManagerConnectionProvider
#hibernate.connection.provider_class net.sf.hibernate.connection.DatasourceConnectionProvider
#hibernate.connection.provider_class net.sf.hibernate.connection.C3P0ConnectionProvider
#hibernate.connection.provider_class net.sf.hibernate.connection.DBCPConnectionProvider
#hibernate.connection.provider_class net.sf.hibernate.connection.ProxoolConnectionProvider
#######################
### Transaction API ###
#######################
## the Transaction API abstracts application code from the underlying JTA or JDBC transactions
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JRun4TransactionManagerLookup
## to use JTATransactionFactory, Hibernate must be able to locate the UserTransaction in JNDI
## default is java:comp/UserTransaction
#jta.UserTransaction jta/usertransaction
#jta.UserTransaction javax.transaction.UserTransaction
#jta.UserTransaction UserTransaction
## to use JTATransactionFactory with JCS caching, Hibernate must be able to obtain the JTA TransactionManager
#hibernate.transaction.manager_lookup_class net.sf.hibernate.transaction.JBossTransactionManagerLookup
#hibernate.transaction.manager_lookup_class net.sf.hibernate.transaction.WeblogicTransactionManagerLookup
#hibernate.transaction.manager_lookup_class net.sf.hibernate.transaction.WebSphereTransactionManagerLookup
#hibernate.transaction.manager_lookup_class net.sf.hibernate.transaction.OrionTransactionManagerLookup
#hibernate.transaction.manager_lookup_class net.sf.hibernate.transaction.ResinTransactionManagerLookup
##############################
### Miscellaneous Settings ###
##############################
## print all generated SQL to the console
hibernate.show_sql true
## specify a JDBC isolation level
#hibernate.connection.isolation 4
## set the JDBC fetch size
#hibernate.jdbc.fetch_size 25
## set the maximum JDBC 2 batch size (a nonzero value enables batching)
#hibernate.jdbc.batch_size 0
hibernate.jdbc.batch_size 30
## enable use of JDBC 2 scrollable ResultSets (specifying a Dialect will cause Hibernate to use a sensible default)
#hibernate.jdbc.use_scrollable_resultset true
## use streams when writing binary types to / from JDBC
hibernate.jdbc.use_streams_for_binary true
## specify a default schema for unqualified tablenames
#hibernate.default_schema test
## use a custom stylesheet for XML generation (if not specified, hibernate-default.xslt will be used)
#hibernate.xml.output_stylesheet C:/Hibernate/net/sf/hibernate/hibernate-default.xslt
## enable outerjoin fetching (specifying a Dialect will cause Hibernate to use sensible default)
#hibernate.use_outer_join false
## set the maximum depth of the outer join fetch tree
# ???
hibernate.max_fetch_depth 75
## enable CGLIB reflection optimizer (enabled by default)
#hibernate.cglib.use_reflection_optimizer false
############
### JNDI ###
############
## specify a JNDI name for the SessionFactory
hibernate.session_factory_name ina_session_factory
## Hibernate uses JNDI to bind a name to a SessionFactory and to look up the JTA UserTransaction;
## if hibernate.jndi.* are not specified, Hibernate will use the default InitialContext() which
## is the best approach in an application server
#JRun4
hibernate.jndi.class jrun.naming.JRunContextFactory
hibernate.jndi.url localhost\:2914
The method I am using to load the class Hierarchy described in the mapping above is as follows:
Code:
/**
* Retrieves a persisted worksheet (i.e. {@link WorksheetHeader} instance) identified by the specified ID.
* @param worksheetID the ID of the worksheet to retrieve
* @return the <code>WorksheetHeader</code> instance.
* @throws Exception
*/
public static WorksheetHeader getWorksheetHeaderByID(int worksheetID, String language) throws Exception {
Session session = DataAccessServiceHelper.getSession();
WorksheetHeader returnHeader = null;
Criteria criteria;
try {
criteria = session.createCriteria(WorksheetHeader.class).add(
Expression.eq(WORKSHEET_ID, new Integer(worksheetID)));
// criteria = criteria.createAlias("products", "prod");
// criteria = criteria.add(Expression.isNull("prod."+PRODUCT_ID));
// criteria = criteria.createAlias("prod.costFactors", "costs");
// criteria = criteria.createAlias("costs.costFactor.calculatedTypeOptions", "calcTypes");
// criteria = criteria.createAlias("costs.costFactor.proratingTypeOptions", "proTypes");
criteria = criteria.createCriteria("products").add(Expression.not(Expression.isNotNull(PRODUCT_ID)));
//// criteria = criteria.createCriteria("costFactors");
// criteria = criteria.add(Expression.eq("calcTypes.language", language));
// criteria = criteria.add(Expression.eq("proTypes.language", language));
// criteria = criteria.createCriteria("costs.costFactor.descriptions");
// criteria = criteria.add(Expression.eq("language", language));
returnHeader = (WorksheetHeader)criteria.list().get(0);
// TODO: remove idiotic force of initialization to stop stupid LazyInitializationException in Hibernate 2.1b2
// Hibernate.initialize(returnHeader);
Hibernate.initialize(returnHeader.getProducts());
// this is really stupid...
returnHeader.toString(); // dumb, very dumb
// Hibernate.initialize(((WorksheetFactor)((WorksheetProduct)returnHeader.getProducts().toArray()[0]).getCostFactors().toArray()[0]).getCostFactor().getDescriptions());
// load the unkeyed worksheet status description seperately
returnHeader.setStatusDescription(getWorksheetStatus(returnHeader.getStatus(), language));
// load the unkeyed foreign exchange status descriptions seperately
returnHeader.setValidForeignStatuses(getWorksheetStatusDescriptions(ForeignExchangeStatus.class, language));
} catch (Throwable t) {
LogFactory.getLog(WorksheetService.class).error("Could not load worksheet " + worksheetID, t);
} finally {
session.close();
}
return returnHeader;
}
This is complete with commented out weird things I have done to try to force initialization to happen and Criteria stuff that doesn't work in the version I have available to me (2.1beta3b). The
returnHeader.toString() line calls a custom toString() method which "prints" out all of the properties and their values which results in navigating through all of my classes that have an overridden toString() thereby forcing initialization of everything.
If I comment out that stupid toString() call I will get LazyInitializationExceptions from any WorksheetHeader instance that has more than one WorksheetProduct in it's Map of WorksheetProducts, but only on the one's with more than one record, the one's with only one record will initialize just fine.
This is what is happening right now, every time I change one little seemingly unrelated (obviously not truly unrelated) thing (such as one lazy=false to lazy=true) the spurious use of proxies for some records and not for others will change to "new" locations and/or types.
I thought perhaps it was related to joining and so I tried bumping up the max_fetch_depth but that didn't seem to have an effect.
Other information (to save reading the config closely); I am using JRun4, SQL Server 2000, and the macromedia driver for SQL Server that comes with JRun4 (I have also tried the j-netdirect driver running under Tomcat 5.0.11 with the same results so I'm sure it has something to do with Hibernate or my configuration thereof and not my container). I am using the latest Struts and the various Struts taglibs. I am using Log4J 1.2.8. I am using the exact jar files that Hibernate ships with copied verbatum from the Hibernate lib folder into my web apps lib folder.
I don't know what else to say but please ask if you need more info and please help if you can.
I've run out of time and I'm at wits end.