Skip to content

Commit 17e50df

Browse files
committed
Fixed a few Hibernate issues: tracking id sequence, null itinerary after load when legs collection is empty.
1 parent 8522046 commit 17e50df

File tree

8 files changed

+108
-5
lines changed

8 files changed

+108
-5
lines changed

dddsample/tracking/core/pom.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
<artifactId>tracking</artifactId>
99
<version>1.2-SNAPSHOT</version>
1010
</parent>
11+
<properties>
12+
<jetty.port>8082</jetty.port>
13+
</properties>
1114
<packaging>war</packaging>
1215
<name>Tracking context: Core</name>
1316
<version>${project.parent.version}</version>

dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/handling/HandlingHistory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
/**
1313
* The handling history of a cargo.
14+
*
15+
* TODO eliminate from 1.2
1416
*/
1517
public class HandlingHistory implements ValueObject<HandlingHistory> {
1618

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package se.citerus.dddsample.tracking.core.infrastructure.persistence.hibernate;
2+
3+
import org.hibernate.event.PostLoadEvent;
4+
import org.hibernate.event.def.DefaultPostLoadEventListener;
5+
import se.citerus.dddsample.tracking.core.domain.model.cargo.Cargo;
6+
7+
import java.lang.reflect.Field;
8+
9+
public class CargoPostLoadEventListener extends DefaultPostLoadEventListener {
10+
11+
private static final Field ITINERARY_FIELD;
12+
static {
13+
try {
14+
ITINERARY_FIELD = Cargo.class.getDeclaredField("itinerary");
15+
ITINERARY_FIELD.setAccessible(true);
16+
} catch (NoSuchFieldException e) {
17+
throw new AssertionError(e);
18+
}
19+
}
20+
21+
@Override
22+
public void onPostLoad(PostLoadEvent event) {
23+
if (event.getEntity() instanceof Cargo) {
24+
/*
25+
* Itinerary is a column-less component with a collection field,
26+
* and there's no way (that I know of) to map this behaviour in metadata.
27+
*
28+
* Hibernate is all about reflection, so helping the mapping along with
29+
* another field manipulation is OK. This avoids the need for a public method
30+
* on Cargo.
31+
*/
32+
Cargo cargo = (Cargo) event.getEntity();
33+
if (cargo.itinerary() != null && cargo.itinerary().legs().isEmpty()) {
34+
try {
35+
ITINERARY_FIELD.set(cargo, null);
36+
} catch (IllegalAccessException e) {
37+
throw new RuntimeException(e);
38+
}
39+
}
40+
}
41+
super.onPostLoad(event);
42+
}
43+
44+
}

dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/infrastructure/persistence/hibernate/DatabaseTrackingIdFactory.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@
66
*/
77
package se.citerus.dddsample.tracking.core.infrastructure.persistence.hibernate;
88

9+
import org.hibernate.HibernateException;
10+
import org.hibernate.Session;
911
import org.hibernate.SessionFactory;
1012
import org.springframework.beans.factory.annotation.Autowired;
13+
import org.springframework.orm.hibernate3.HibernateCallback;
14+
import org.springframework.orm.hibernate3.HibernateTemplate;
1115
import org.springframework.stereotype.Repository;
1216
import se.citerus.dddsample.tracking.core.domain.model.cargo.TrackingId;
1317
import se.citerus.dddsample.tracking.core.domain.model.cargo.TrackingIdFactory;
1418

19+
import javax.annotation.PostConstruct;
20+
import java.math.BigInteger;
21+
import java.sql.SQLException;
22+
1523
@Repository
1624
public class DatabaseTrackingIdFactory implements TrackingIdFactory {
1725

@@ -23,14 +31,26 @@ public DatabaseTrackingIdFactory(final SessionFactory sessionFactory) {
2331
this.sessionFactory = sessionFactory;
2432
}
2533

34+
@PostConstruct
35+
public void createSequence() {
36+
final HibernateTemplate template = new HibernateTemplate(sessionFactory);
37+
final HibernateCallback callback = new HibernateCallback() {
38+
@Override
39+
public Object doInHibernate(final Session session) throws HibernateException, SQLException {
40+
return session.createSQLQuery("create sequence " + SEQUENCE_NAME + " as bigint start with 1").executeUpdate();
41+
}
42+
};
43+
44+
template.execute(callback);
45+
}
46+
2647
@Override
2748
public TrackingId nextTrackingId() {
28-
final Long seq = (Long) sessionFactory.getCurrentSession().
29-
createSQLQuery("select next_value from system_sequences where sequence_name = ?").
30-
setParameter(1, SEQUENCE_NAME).
49+
final BigInteger seq = (BigInteger) sessionFactory.getCurrentSession().
50+
createSQLQuery("call next value for " + SEQUENCE_NAME).
3151
uniqueResult();
3252

33-
return new TrackingId(seq);
53+
return new TrackingId(seq.longValue());
3454
}
3555

3656
}

dddsample/tracking/core/src/main/resources/contexts/context-infrastructure-persistence.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,15 @@
2424
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
2525
<property name="dataSource" ref="dataSource"/>
2626
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
27+
<property name="eventListeners">
28+
<map>
29+
<entry key="post-load" value-ref="cargoPostLoadEventListener"/>
30+
</map>
31+
</property>
2732
</bean>
2833

34+
<bean id="cargoPostLoadEventListener" class="se.citerus.dddsample.tracking.core.infrastructure.persistence.hibernate.CargoPostLoadEventListener"/>
35+
2936
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
3037
<property name="sessionFactory" ref="sessionFactory"/>
3138
</bean>

dddsample/tracking/core/src/main/resources/se/citerus/dddsample/tracking/core/infrastructure/persistence/hibernate/Cargo.hbm.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
</component>
2929

3030
<component name="itinerary">
31-
<!-- cascade=all-delete-orphan would be better, but it doesn't seem to work inside a component -->
3231
<list name="legs" lazy="true" cascade="all">
3332
<key column="cargo_id" foreign-key="itinerary_fk"/>
3433
<index column="leg_index"/>

dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/infrastructure/persistence/hibernate/CargoRepositoryTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ public void testSave() {
107107
Cargo cargo = new Cargo(trackingId, new RouteSpecification(origin, destination, new Date()));
108108
cargoRepository.store(cargo);
109109

110+
getSession().flush();
111+
getSession().clear();
112+
113+
cargo = cargoRepository.find(trackingId);
114+
assertNull(cargo.itinerary());
115+
110116
cargo.assignToRoute(new Itinerary(
111117
Leg.deriveLeg(
112118
voyageRepository.find(new VoyageNumber("0101")),
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package se.citerus.dddsample.tracking.core.infrastructure.persistence.hibernate;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import se.citerus.dddsample.tracking.core.domain.model.cargo.TrackingId;
5+
6+
/**
7+
*
8+
*/
9+
public class DatabaseTrackingIdFactoryTest extends AbstractRepositoryTest {
10+
11+
@Autowired
12+
DatabaseTrackingIdFactory trackingIdFactory;
13+
14+
public void testNext() throws Exception {
15+
TrackingId id1 = trackingIdFactory.nextTrackingId();
16+
TrackingId id2 = trackingIdFactory.nextTrackingId();
17+
assertNotNull(id1);
18+
assertNotNull(id2);
19+
assertFalse(id1.equals(id2));
20+
}
21+
22+
}

0 commit comments

Comments
 (0)