Wednesday, September 15, 2010

Hibernate: Parent and Children --> Retrieving Children using a Filter

Same classes as the previous post but this time to avoid an issue with lazy loading the children outside of a closed Hibernate session, load the parent and its children together in the DAO.

This is slightly more complicated as the data model class now has a status attribute (true || false). Either load all the data or only load the data that is active (If a parent is not active, it is not loaded so none of its children is loaded either).

Using the Criteria API, there doesn't seem to be a way to specify a join criteria on the children join.
You can do it easily using a query:
from Cat as cat
left join cat.kittens as kitten
with kitten.bodyWeight > 10.0
Hence the use of a filter when using the Criteria API. The filter either restricts the children to be active or just loads all of them.

@Repository
public class HibernatePropertyTypeDaoImpl extends BaseHibernateDao implements PropertyTypeDao {
  
  private static final String ACTIVE_WHERE_CLAUSE = "where active = true";
  
  private static Logger LOG = LoggerFactory.getLogger(HibernatePropertyTypeDaoImpl.class);

  @Override
  @SuppressWarnings("unchecked")
  public List getCommercialPropertyTypes(boolean onlyActive) {
    List commercialTypes = (List) getPropertyTypesWithoutChildren(CommercialPropertyType.class, onlyActive);
    for(CommercialPropertyType commercialType : commercialTypes) {
      getChildren(commercialType, onlyActive);
    }
    return commercialTypes;
  }
  
  @SuppressWarnings("unchecked")
  private List getPropertyTypesWithoutChildren(Class clazz, boolean onlyActive) {
    Criteria criteria = sessionFactory.getCurrentSession().createCriteria(clazz); 
    criteria.add(Restrictions.isNull("parent"));
    if (onlyActive) {
      criteria.add(Restrictions.eq("active", true));
    }
    criteria.addOrder(Order.asc("id"));
    return criteria.list();
  }

  @SuppressWarnings("unchecked")
  protected void getChildren(CommercialPropertyType type, boolean onlyActive) {
    List children = null;
    if (onlyActive) {
      children = sessionFactory.getCurrentSession()
                               .createFilter(type.getChildren(), ACTIVE_WHERE_CLAUSE)
                               .list();
    } else {
      children = sessionFactory.getCurrentSession()
                               .createFilter(type.getChildren(), "")
                               .list();
    }
    type.setChildren(children);
    LOG.debug("Set children:"+children);
  } 
}

Tuesday, September 14, 2010

Hibernate: Parent and Children

Mapping Model Class:

A single class is used to represent both the parent and children -> CommercialPropertyType.
(The super class PropertyType just provides additional attributes and is not important.)

@Entity
@Table(name = "commercial_property_type")
public class CommercialPropertyType extends PropertyType {

private CommercialPropertyType parent;
private List children;

...

@ManyToOne
@JoinColumn(name = "parent_id")
public CommercialPropertyType getParent() {
return parent;
}

public void setParent(CommercialPropertyType parent) {
this.parent = parent;
}

@OneToMany(cascade = { CascadeType.ALL })
@JoinColumns({ @JoinColumn(name = "parent_id") })
public List getChildren() {
return children;
}

public void setChildren(List children) {
this.children = children;
}
}

Retrieving Data:

Example DAO method to retrieve the top-level parent data with their children data.

@Repository
public class HibernatePropertyTypeDaoImpl extends BaseHibernateDao implements PropertyTypeDao {

@Override
@SuppressWarnings("unchecked")
public List getCommercialPropertyTypes() {
return (List) getPropertyTypesWithChildren(CommercialPropertyType.class);
}

@SuppressWarnings("unchecked")
private List getPropertyTypesWithChildren(Class clazz) {
Criteria criteria = this.sessionFactory.getCurrentSession().createCriteria(clazz);
//Only get parents
criteria.add(Restrictions.isNull("parent"));
//Get the parent's children if there are any
criteria.createAlias("children", "children", CriteriaSpecification.LEFT_JOIN);
criteria.addOrder(Order.asc("id"));
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
return criteria.list();
}
}

Thursday, May 6, 2010

Hibernate orderBy gotcha

I just spent the good part of an hour trying to figure out why the "order by" clause in my HQL query was causing a SQLGrammarException "could not execute query".

The SQL worked perfectly, I was stumped. Google couldn't even tell me the answer! I used the time-tested method of try everything and figure out the pattern to solve this one....

The answer was that if you are specifying the "select" clause, the attribute that you are ordering by must appear in the select list.

So, to continue with the example in the hibernate 3.3 documentation
select cat.sex from DomesticCat cat
order by cat.name asc, cat.weight desc, cat.birthdate
is incorrect, but
select cat.sex, cat.name, cat.weight, cat.birthdate from DomesticCat cat
order by cat.name asc, cat.weight desc, cat.birthdate
works.

Thursday, April 22, 2010

Converting an OutputStream to an InputStream

This old problem again... I need an input stream in my test - but how do I create it? This guy knows. Thanks!

final PipedOutputStream pout = new PipedOutputStream();
new Thread(new Runnable(){
public void run(){
DataOutput output = new DataOutputStream(pout);
// write to output stream here
}
}
).start();
DataInputStream in = new DataInputStream(new PipedInputStream(pout));

Tuesday, November 3, 2009

Windows vs Linux

Now I am working on windows again, I have come across the dreaded ^M (windows end of line character)... To strip these characters, on linux, open VI and type :1,$ s/{ctrl-V}{ctrl-M}//

Monday, November 2, 2009

Simple performance logging with Spring

We had a problem recently where a call/response to the server was taking 10 seconds. We needed to figure out where the delay was, and the perfect way to do that was using the Spring PerformanceMonitorInterceptor. With Spring 2.0, adding it is a breeze.

In the applicationContext.xml

<bean id="timingLogger" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor">

<aop:aspectj-autoproxy>
<aop:config>
<aop:advisor pointcut="execution(* com.yourcompany.application.dao.*.*(..))" ref="timingLogger">
</aop:advisor>
</aop:config>
</aop:aspectj-autoproxy>


and then in log4j.properties

log4j.logger.org.springframework.aop.interceptor.PerformanceMonitorInterceptor=TRACE, stdouttrace
log4j.appender.stdouttrace=org.apache.log4j.ConsoleAppender
log4j.appender.stdouttrace.layout=org.apache.log4j.PatternLayout
log4j.appender.stdouttrace.layout.ConversionPattern=%m%n


We found the problem was not in the application, but with the dev server, which was not configured properly to resolve hosts...

Thursday, October 15, 2009

GWT 1.7 (Hosted Mode): Using a data source

Spring Data Source:
Typically your Spring configuration files specify a data source that is used by your persistence layer to access the physical database.

In order to avoid having to keep changing the data source's details per environment in the Spring config files (dev, test, qa, prod), the easy way is to use a JNDI look up for your data source. Each environment will then be responsible for having the correct data source set up for use.

From the Spring configuration file:
<!-- The data source which is looked up via JNDI -->
  <jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyApp" lookup-on-startup="true" />

Data Source in web application
In the web.xml file, define the data source for use:
<!-- The data source that the application uses to access the current database -->
  <resource-ref>
        <description>The Oracle database data source.</description>
        <res-ref-name>jdbc/MyApp</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
  </resource-ref> 

GWT 1.7 (Hosted Mode):
Since GWT 1,7 uses Jetty to run in hosted mode, you need to set up a Jetty data source so that Spring has its data source to use when the application is run.

Create a file in the /war/WEB-INF folder called jetty-web.xml and define your data source in there:
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
"http://jetty.mortbay.org/configure.dtd">
<Configure class="org.mortbay.jetty.webapp.WebAppContext">
  <New id="MyApp" class="org.mortbay.jetty.plus.naming.Resource"> 
    <Arg>jdbc/MyApp</Arg>
    <Arg>
      <New class="oracle.jdbc.pool.OracleDataSource">
        <Set name="user">mast</Set>
        <Set name="password">mast</Set>
        <Set name="URL">jdbc:oracle:thin:@localhost:1521:XE</Set>      
        <Set name="connectionCachingEnabled">true</Set> 
     </New>
    </Arg>
  </New>
</Configure>

Running the GWT Application (Eclipse using Google's GWT Plug-in):
You need to add two Jetty jars to your Eclipse's project classpath, namely:
jetty-name-6.1.x.jar and jetty-plus-6.1.x.jar.
I downloaded Jetty 6.1.19 and used the jars from it.

When you run the application (Right-click the project > Run As > Web Application), you need to modify the Run Configuration slightly.
To modify an existing instance of the application's Run Configuration, use Eclipse's menu option: Run > Run Configurations and select the correct GWT application instance.

On the Arguments tab, add the following VM argument:
-Djava.naming.factory.initial=org.mortbay.naming.InitialContextFactory

Now when you run the application in GWT hosted mode, Spring should have no trouble finding and using the specified data source.

Standalone Mode
Obviously you can do a similar set up for standlone mode (Tomcat or whatever web server you are using) - define the data source in the appropriate way so it is available for the application to use.

Note: the jetty-web.xml file obviously does not need to be deployed with the application into another web container such as Tomcat.