Hi,
I wrote an Interceptor with an instantiate that uses a Guice injector to construct entity objects. I'm seeing Hibernate call the default constructor on the entity object anyway, so it's not always using Interceptor.instantiate to do the work.
What are these default-construct objects for? What functions might be called on the default objects? Does hibernate ever expose any of these objects as the result of a load or query?
My objects' getters/setters for the persistent properties should be safe without the injected dependencies. However, other functionality in the entities needs the injected dependencies, and I need to ensure that my end user never gets an object constructed with the default constructor.
I'm using Hibernate 3.2.6 and Annotations 3.3.1. I've included the complete source for the interceptor and a simple test that reveals my problem (see the note in the PojoInjectorTest.Pojo.Pojo()).
PojoInjector:
Code:
import com.google.inject.Injector;
import org.hibernate.CallbackException;
import org.hibernate.EmptyInterceptor;
import org.hibernate.EntityMode;
import org.hibernate.SessionFactory;
import java.io.Serializable;
class PojoInjector extends EmptyInterceptor {
private Injector injector = null;
private SessionFactory sf = null;
/*
* The PojoInjector requires the SessionFactory to set identifiers, but the
* PojoInjector is required to build the SesisonFactory, so we can't inject
* this into a constructor.
*/
void setSessionFactory(SessionFactory sf) {
this.sf = sf;
}
void setInjector(Injector injector) {
this.injector = injector;
}
@Override
public Object instantiate(String entityName, EntityMode entityMode, Serializable id)
throws CallbackException {
if (entityMode != EntityMode.POJO) {
return super.instantiate(entityName, entityMode, id);
}
try {
Class<?> clazz = PojoInjector.class.getClassLoader().loadClass(entityName);
Object obj = injector.getInstance(clazz);
sf.getClassMetadata(entityName).setIdentifier(obj, id, entityMode);
return obj;
} catch (Exception exn) {
String msg = String.format("Could not inject %s", entityName);
throw new CallbackException(msg, exn);
}
}
}
PojoInjectorTest.javaCode:
import static javax.persistence.GenerationType.AUTO;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.classextension.EasyMock.createStrictControl;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import junit.framework.TestCase;
import org.easymock.IMocksControl;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author topher@google.com (Your Name Here)
*
*/
public class PojoInjectorTest extends TestCase {
// Something our POJO depends on.
public static interface Dependency {
public void yepPojoWasInjected();
}
// Our POJO for an entity.
@Entity
public static class Pojo {
@Id @GeneratedValue(strategy = AUTO)
private int id;
public Pojo() {
// This gets called twice, but I expected never.
}
@Inject
public Pojo(Dependency dep) {
dep.yepPojoWasInjected();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
// Bind a mock to the dependency.
private static class Module extends AbstractModule {
private Dependency dep;
private Module(Dependency dep) {
this.dep = dep;
}
@Override
protected void configure() {
bind(Dependency.class).toInstance(dep);
}
}
public void testLoadInjectsPojo() {
/*
* Setup
*/
IMocksControl c = createStrictControl();
Dependency dep = c.createMock(Dependency.class);
dep.yepPojoWasInjected();
expectLastCall().times(2);
Injector injector = Guice.createInjector(new Module(dep));
PojoInjector pojoInjector = new PojoInjector();
SessionFactory sf = new AnnotationConfiguration()
.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver")
.setProperty("hibernate.connection.url", "jdbc:hsqldb:mem:pojoInjectorTest")
.setProperty("hibernate.connection.username", "sa")
.setProperty("hibernate.connection.password", "")
.setProperty("hibernate.hbm2ddl.auto", "create-drop")
.addAnnotatedClass(Pojo.class)
.setInterceptor(pojoInjector)
.buildSessionFactory();
pojoInjector.setSessionFactory(sf);
pojoInjector.setInjector(injector);
/*
* Create an entity to load later.
*/
c.replay();
Session s = sf.openSession();
Transaction t = s.beginTransaction();
Pojo pj = injector.getInstance(Pojo.class);
s.save(pj);
t.commit();
s.close();
int savedId = pj.getId();
/*
* Now load the entity.
*/
s = sf.openSession();
t = s.beginTransaction();
pj = (Pojo) s.load(Pojo.class, savedId);
assertEquals(pj.getId(), savedId);
t.commit();
s.close();
c.verify();
}
}