This is what I do:
@Repository
@Transactional(propagation = Propagation.SUPPORTS)
public class ProfileDAOHibernateImpl implements ProfileDAO {
@Autowired
private SessionFactory sessionFactory;
private Session currentSession() {
return sessionFactory.getCurrentSession();
}
@Override
public Profile getProfile(String name) {
return (Profile) currentSession().createCriteria(Profile.class)
.add(Restrictions.eq("name", name)).uniqueResult();
}
...
}
Profile entity:
@Entity
@Table(name = "profiles")
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int Id;
@Column(name = "name", unique = true)
private String name;
@ManyToOne(optional = false, fetch = FetchType.EAGER)
@JoinColumn(name="partner_id", nullable = false)
private Partner partner;
...
}
Partner entity:
@Entity
@Table(name = "partners")
public class Partner {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@OneToMany(fetch = FetchType.LAZY, cascade={CascadeType.ALL}, mappedBy = "partner")
@OrderBy(value = "dateCreated desc")
private Set<Profile> profiles = new HashSet<Profile>();
@ManyToMany(fetch = FetchType.EAGER)
@OrderBy(value = "name asc")
@JoinTable(name = "partner_service",
joinColumns = { @JoinColumn(name = "partner_id") },
inverseJoinColumns = { @JoinColumn(name = "service_id") })
private Set<Service> services = new HashSet<Service>();
...
}
Service entity:
@Entity
@Table(name = "services")
public class Service {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "partner_service",
joinColumns = { @JoinColumn(name = "service_id") },
inverseJoinColumns = { @JoinColumn(name = "partner_id") })
private Set<Partner> partners = new HashSet<Partner>();
...
}
Here are my hibernate/datasource configurations:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="poolPreparedStatements" value="true" />
<property name="maxActive" value="20" />
<property name="initialSize" value="2" />
<property name="maxIdle" value="2" />
</bean>
<bean id="profileDAO" class="com.eniro.partnerapi.dao.impl.ProfileDAOHibernateImpl" />
<!-- Hibernate session factory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.eniro.partnerapi.model" />
<property name="hibernateProperties">
<props>
<prop key="dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.hbm2ddl.auto">verify</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.jdbc.batch_size">30</prop>
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="dataSource" ref="dataSource" />
<property name="sessionFactory" ref="sessionFactory" />
</bean>
- A Partner has a OneToMany to Profiles (and vice versa)
- A Partner has a ManyToMany to Services
What I’m trying to achieve is that when I load a profile, I want its owning Partner to be loaded, and the Partner’s Services at the same time. And yes, I want them to be eagerly loaded since they are rarely of great sets, and they are almost always needed all at the same time in this system.
An example scenario is:
- Partner A has 3 profiles
- Partner A has 5 services
- The partners first Profile is fetched (profileX), at the same time the Partner and the Partner’s Services are loaded.
Here is where my problem occurs (in the getProfile call in my DAO): Sometimes only the first service (based on id) is loaded from the database, and sometimes all 5 services are loaded.
What can be the cause of this? Anyone has any experience or anything for me to go on?
EDIT – adding additional info
We are using:
- Hibernate 3.6.7.Final
- MySQL connector 5.1.16
- Spring 3.0.5.RELEASE
- Quartz 1.6.1
- MySQL 5.1.x
- Tomcat 6.33
In a distributed environment where the application is run on two separate servers.
Alright, we managed to finally find the problem. It turns out it’s not necessarily Hibernates fault.
We have a Quartz scheduler which was handling jobs on the side. The problem occurred once the scheduler (org.springframework.scheduling.quartz.SchedulerFactoryBean) started up. This is fine. However, we have two instances of the same application running in production, so the scheduler was using the database to synchronize between the two applications to keep a job from running at the same time from two instances.
However, this Quartz scheduler was communicating with the database in his own way (didn’t find any code for it), which lead to clashes between Hibernates and Quartzs connections and thus giving the strange loading behaviour from the DAOs.
What exactly happened I can’t say in detail, but we removed the DB dependency of Quartz and just used namespace to handle all jobs. After that, the problem was gone.
Warning to people using Hibernate/JDBC and Quartz scheduling between distributed applications: Be careful with how the components handle connections to the database.