I've written code that looks like listing 16.1 on page 699 (2nd edition). The code loads an Item (my code GETs a Product), instantiates a Bid and adds it to the Item (my code instantiates a Version and adds it to the product). It does this all within a transaction context, and on page 701 the notes for step 5 say that you don't have to save it manually, it's saved using transitive persistence (cascading from Item to Bid).
As near as I can tell, my Product and Version classes are structured in exactly the same way, using the same annotations, as the CaveatEmptor JPA example code. When it executes, I see that the new Version object is added to the in-memory list of Versions on the Product. But it never gets persisted. If I call session.save(ver) (on the new Version object), it properly saves. However, I shouldn't have to call that.
I get no exceptions, nor warnings in the log. I've included the last part of the log. I'm using Spring to manage some of this, otherwise it's happening in a struts action.
One thing that stands out: the logs say flush mode is NEVER. However, I never set the flush mode in my code, and the book says the default flush mode is AUTO. Perhaps Spring's AnnotationSessionFactoryBean is setting the flush mode? I've included the Spring config below, too.
Thanks for any help!
Hibernate version: 3.2.6ga, annotations 3.3.0ga
Mapping documents: none (using annotations).
Code:
@Entity
@Table
@Proxy(lazy = true)
public
class
Product implements Serializable
{
public Long getId() { return mId; }
public void setId(Long inVal) { mId = inVal; }
public Set<Version> getVersions() { return mVersions; }
public void setVersions(Set<Version> inVersions) { mVersions = inVersions; }
public
void
add(Version inVersion)
{
//inVersion.setProduct(this);
mVersions.add(inVersion);
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long mId;
//@OneToMany(mappedBy = "mProduct", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
@OneToMany(mappedBy = "mProduct", cascade = CascadeType.ALL)
@OrderBy("mPubDate desc")
private Set<Version> mVersions = new HashSet<Version>();
}
Code:
@Entity
@Table
@Proxy(lazy = true)
public
class
Version implements Serializable
{
public Long getId() { return mId; }
public void setId(Long inVal) { mId = inVal; }
public Product getProduct() { return mProduct; }
//public void setProduct(Product inVal) { mProduct = inVal; }
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long mId;
@ManyToOne
@JoinColumn(name = "mProductId", nullable = false, updatable = false, insertable = false)
private Product mProduct;
}
Code between sessionFactory.openSession() and session.close():Code:
Session session = service.getSessionFactory().getCurrentSession();
Transaction tx = null;
try
{
tx = session.beginTransaction();
// Fetch the product…
Product prod = service.getProduct(pid);
// Create the new version…
Version ver = new Version();
ver.setUpdateVersion(uv);
ver.setPubDate(Calendar.getInstance());
ver.setTitle(prod.getName() + " v" + uv); // Create a title from the product name and version
prod.add(ver);
//session.save(ver);
tx.commit();
inReq.setAttribute("prod", prod);
inReq.setAttribute("ver", ver);
}
catch (Exception e)
{
String msg = "Exception creating new upload";
sLogger.error(msg, e);
ActionMessage err = new ActionMessage("error.generic", msg);
ActionMessages errs = getErrors(inReq);
errs.add(ActionMessages.GLOBAL_MESSAGE, err);
saveErrors(inReq, errs);
if (tx != null) tx.rollback();
return new ActionForward(inMapping.getInput());
}
finally
{
//session.close();
}
Spring config:Code:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/versdb"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="annotatedClasses">
<list>
<value>com.latencyzero.versdb.model.Product</value>
<value>com.latencyzero.versdb.model.Version</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="transactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="txManager"/>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="service" parent="transactionProxy">
<property name="target">
<bean class="com.latencyzero.versdb.dao.Service">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</property>
</bean>
</beans>
Full stack trace of any exception that occurs: no exceptions
Name and version of the database you are using: MySQL 5.1
The generated SQL (show_sql=true): Debug level Hibernate log excerpt:Code:
20:58:01.827 DEBUG [ ] loading.LoadContexts (LoadContexts.java:269) attempting to locate loading collection entry [CollectionKey[com.latencyzero.versdb.model.Product.mVersions#1]] in any result-set context
20:58:01.827 DEBUG [ ] loading.LoadContexts (LoadContexts.java:277) collection [CollectionKey[com.latencyzero.versdb.model.Product.mVersions#1]] located in load context
20:58:01.828 DEBUG [ ] loading.CollectionLoadContext (CollectionLoadContext.java:186) removing collection load entry [org.hibernate.engine.loading.LoadingCollectionEntry<rs=com.mysql.jdbc.ResultSetImpl@8b6551, coll=[com.latencyzero.versdb.model.Product.mVersions#1]>@bd63e0]
20:58:01.828 DEBUG [ ] loading.CollectionLoadContext (CollectionLoadContext.java:217) 1 collections were found in result set for role: com.latencyzero.versdb.model.Product.mVersions
20:58:01.828 DEBUG [ ] loading.CollectionLoadContext (CollectionLoadContext.java:232) ending loading collection [org.hibernate.engine.loading.LoadingCollectionEntry<rs=com.mysql.jdbc.ResultSetImpl@8b6551, coll=[com.latencyzero.versdb.model.Product.mVersions#1]>@bd63e0]
20:58:01.829 DEBUG [ ] loading.CollectionLoadContext (CollectionLoadContext.java:263) collection fully initialized: [com.latencyzero.versdb.model.Product.mVersions#1]
20:58:01.829 DEBUG [ ] loading.CollectionLoadContext (CollectionLoadContext.java:226) 1 collections initialized for role: com.latencyzero.versdb.model.Product.mVersions
20:58:01.829 DEBUG [ ] engine.StatefulPersistenceContext (StatefulPersistenceContext.java:837) initializing non-lazy collections
20:58:01.829 DEBUG [ ] loader.Loader (Loader.java:2010) done loading collection
20:58:01.830 DEBUG [ ] def.DefaultInitializeCollectionEventListener (DefaultInitializeCollectionEventListener.java:64) collection initialized
20:58:01.830 DEBUG [ ] versdb.UploadReleaseSubmitAction (UploadReleaseSubmitAction.java:104) ================================== commit
20:58:01.830 DEBUG [ ] transaction.JDBCTransaction (JDBCTransaction.java:103) commit
20:58:01.831 DEBUG [ ] impl.SessionImpl (SessionImpl.java:337) automatically flushing session
20:58:01.831 DEBUG [ ] jdbc.JDBCContext (JDBCContext.java:205) before transaction completion
20:58:01.831 DEBUG [ ] impl.SessionImpl (SessionImpl.java:393) before transaction completion
20:58:01.839 DEBUG [ ] transaction.JDBCTransaction (JDBCTransaction.java:193) re-enabling autocommit
20:58:01.848 DEBUG [ ] transaction.JDBCTransaction (JDBCTransaction.java:116) committed JDBC Connection
20:58:01.849 DEBUG [ ] jdbc.JDBCContext (JDBCContext.java:219) after transaction completion
20:58:01.849 DEBUG [ ] jdbc.ConnectionManager (ConnectionManager.java:302) transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
20:58:01.852 DEBUG [ ] impl.SessionImpl (SessionImpl.java:422) after transaction completion
20:58:01.852 DEBUG [ ] impl.SessionImpl (SessionImpl.java:353) automatically closing session
20:58:01.853 DEBUG [ ] impl.SessionImpl (SessionImpl.java:273) closing session
20:58:01.853 DEBUG [ ] jdbc.ConnectionManager (ConnectionManager.java:380) performing cleanup
20:58:01.853 DEBUG [ ] jdbc.ConnectionManager (ConnectionManager.java:441) releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
20:58:01.864 DEBUG [ ] jdbc.JDBCContext (JDBCContext.java:219) after transaction completion
20:58:01.864 DEBUG [ ] jdbc.ConnectionManager (ConnectionManager.java:302) transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
20:58:01.865 DEBUG [ ] impl.SessionImpl (SessionImpl.java:422) after transaction completion
20:58:01.866 DEBUG [ ] impl.SessionImpl (SessionImpl.java:273) closing session
20:58:01.866 DEBUG [ ] jdbc.ConnectionManager (ConnectionManager.java:380) performing cleanup
20:58:01.866 DEBUG [ ] jdbc.ConnectionManager (ConnectionManager.java:441) releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
20:58:01.867 DEBUG [ ] jdbc.JDBCContext (JDBCContext.java:219) after transaction completion
20:58:01.871 DEBUG [ ] jdbc.ConnectionManager (ConnectionManager.java:302) transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
20:58:01.871 DEBUG [ ] impl.SessionImpl (SessionImpl.java:422) after transaction completion
20:58:01.876 DEBUG [ ] impl.SessionImpl (SessionImpl.java:220) opened session at timestamp: 12058990818
20:58:01.876 DEBUG [ ] impl.SessionImpl (SessionImpl.java:1289) setting flush mode to: NEVER
20:58:01.878 DEBUG [ ] impl.SessionImpl (SessionImpl.java:273) closing session
20:58:01.879 DEBUG [ ] impl.SessionImpl (SessionImpl.java:220) opened session at timestamp: 12058990818
20:58:01.879 DEBUG [ ] impl.SessionImpl (SessionImpl.java:1289) setting flush mode to: NEVER
20:58:01.880 DEBUG [ ] impl.SessionImpl (SessionImpl.java:273) closing session
20:58:01.880 DEBUG [ ] jdbc.ConnectionManager (ConnectionManager.java:375) connection already null in cleanup : no action
20:58:01.879 DEBUG [ ] jdbc.ConnectionManager (ConnectionManager.java:375) connection already null in cleanup : no action