Просмотр исходного кода

Fixed a few Hibernate issues: tracking id sequence, null itinerary after load when legs collection is empty.

peter_backlund 16 лет назад
Родитель
Сommit
17e50df3e2

+ 3
- 0
dddsample/tracking/core/pom.xml Просмотреть файл

@@ -8,6 +8,9 @@
8 8
     <artifactId>tracking</artifactId>
9 9
     <version>1.2-SNAPSHOT</version>
10 10
   </parent>
11
+  <properties>
12
+    <jetty.port>8082</jetty.port>
13
+  </properties>
11 14
   <packaging>war</packaging>
12 15
   <name>Tracking context: Core</name>
13 16
   <version>${project.parent.version}</version>

+ 2
- 0
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/handling/HandlingHistory.java Просмотреть файл

@@ -11,6 +11,8 @@ import static java.util.Collections.sort;
11 11
 
12 12
 /**
13 13
  * The handling history of a cargo.
14
+ *
15
+ * TODO eliminate from 1.2
14 16
  */
15 17
 public class HandlingHistory implements ValueObject<HandlingHistory> {
16 18
 

+ 44
- 0
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/infrastructure/persistence/hibernate/CargoPostLoadEventListener.java Просмотреть файл

@@ -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
+}

+ 24
- 4
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/infrastructure/persistence/hibernate/DatabaseTrackingIdFactory.java Просмотреть файл

@@ -6,12 +6,20 @@
6 6
  */
7 7
 package se.citerus.dddsample.tracking.core.infrastructure.persistence.hibernate;
8 8
 
9
+import org.hibernate.HibernateException;
10
+import org.hibernate.Session;
9 11
 import org.hibernate.SessionFactory;
10 12
 import org.springframework.beans.factory.annotation.Autowired;
13
+import org.springframework.orm.hibernate3.HibernateCallback;
14
+import org.springframework.orm.hibernate3.HibernateTemplate;
11 15
 import org.springframework.stereotype.Repository;
12 16
 import se.citerus.dddsample.tracking.core.domain.model.cargo.TrackingId;
13 17
 import se.citerus.dddsample.tracking.core.domain.model.cargo.TrackingIdFactory;
14 18
 
19
+import javax.annotation.PostConstruct;
20
+import java.math.BigInteger;
21
+import java.sql.SQLException;
22
+
15 23
 @Repository
16 24
 public class DatabaseTrackingIdFactory implements TrackingIdFactory {
17 25
 
@@ -23,14 +31,26 @@ public class DatabaseTrackingIdFactory implements TrackingIdFactory {
23 31
     this.sessionFactory = sessionFactory;
24 32
   }
25 33
 
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
+
26 47
   @Override
27 48
   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).
31 51
       uniqueResult();
32 52
 
33
-    return new TrackingId(seq);
53
+    return new TrackingId(seq.longValue());
34 54
   }
35 55
 
36 56
 }

+ 7
- 0
dddsample/tracking/core/src/main/resources/contexts/context-infrastructure-persistence.xml Просмотреть файл

@@ -24,8 +24,15 @@
24 24
   <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
25 25
     <property name="dataSource" ref="dataSource"/>
26 26
     <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>
27 32
   </bean>
28 33
 
34
+  <bean id="cargoPostLoadEventListener" class="se.citerus.dddsample.tracking.core.infrastructure.persistence.hibernate.CargoPostLoadEventListener"/>
35
+
29 36
   <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
30 37
     <property name="sessionFactory" ref="sessionFactory"/>
31 38
   </bean>

+ 0
- 1
dddsample/tracking/core/src/main/resources/se/citerus/dddsample/tracking/core/infrastructure/persistence/hibernate/Cargo.hbm.xml Просмотреть файл

@@ -28,7 +28,6 @@
28 28
     </component>
29 29
 
30 30
     <component name="itinerary">
31
-      <!-- cascade=all-delete-orphan would be better, but it doesn't seem to work inside a component -->
32 31
       <list name="legs" lazy="true" cascade="all">
33 32
         <key column="cargo_id" foreign-key="itinerary_fk"/>
34 33
         <index column="leg_index"/>

+ 6
- 0
dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/infrastructure/persistence/hibernate/CargoRepositoryTest.java Просмотреть файл

@@ -107,6 +107,12 @@ public class CargoRepositoryTest extends AbstractRepositoryTest {
107 107
     Cargo cargo = new Cargo(trackingId, new RouteSpecification(origin, destination, new Date()));
108 108
     cargoRepository.store(cargo);
109 109
 
110
+    getSession().flush();
111
+    getSession().clear();
112
+
113
+    cargo = cargoRepository.find(trackingId);
114
+    assertNull(cargo.itinerary());
115
+
110 116
     cargo.assignToRoute(new Itinerary(
111 117
       Leg.deriveLeg(
112 118
         voyageRepository.find(new VoyageNumber("0101")),

+ 22
- 0
dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/infrastructure/persistence/hibernate/DatabaseTrackingIdFactoryTest.java Просмотреть файл

@@ -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
+}