44import dao .ElementDao ;
55import javabean .JavaBeanHelper ;
66import jpa .manager .JPAManager ;
7+ import org .hibernate .Hibernate ;
8+ import org .omg .sysml .internal .CommitIndex ;
9+ import org .omg .sysml .internal .impl .CommitIndexImpl ;
10+ import org .omg .sysml .internal .impl .CommitIndexImpl_ ;
711import org .omg .sysml .lifecycle .Commit ;
12+ import org .omg .sysml .lifecycle .ElementVersion ;
13+ import org .omg .sysml .lifecycle .impl .ElementIdentityImpl ;
14+ import org .omg .sysml .lifecycle .impl .ElementIdentityImpl_ ;
15+ import org .omg .sysml .lifecycle .impl .ElementVersionImpl ;
16+ import org .omg .sysml .lifecycle .impl .ElementVersionImpl_ ;
817import org .omg .sysml .metamodel .Element ;
918import org .omg .sysml .metamodel .impl .MofObjectImpl ;
1019import org .omg .sysml .metamodel .impl .MofObjectImpl_ ;
1120
1221import javax .inject .Inject ;
1322import javax .inject .Singleton ;
23+ import javax .persistence .EntityManager ;
24+ import javax .persistence .EntityTransaction ;
1425import javax .persistence .NoResultException ;
1526import javax .persistence .criteria .*;
1627import java .util .*;
28+ import java .util .concurrent .ConcurrentHashMap ;
1729import java .util .function .Consumer ;
1830import java .util .function .Function ;
1931import java .util .stream .Collectors ;
2234@ Singleton
2335public class JpaElementDao extends JpaDao <Element > implements ElementDao {
2436 // TODO Explore alternative to serializing lazy entity attributes that doesn't involve resolving all proxies one level.
25- static Consumer <Element > PROXY_RESOLVER = element -> JavaBeanHelper .getBeanPropertyValues (element ).values ().stream ().flatMap (o -> o instanceof Collection ? ((Collection <?>) o ).stream () : Stream .of (o )).forEach (o -> {} );
37+ static Consumer <Element > PROXY_RESOLVER = element -> JavaBeanHelper .getBeanPropertyValues (element ).values ().stream ().flatMap (o -> o instanceof Collection ? ((Collection <?>) o ).stream () : Stream .of (o )).filter (o -> o instanceof Element ). map ( o -> ( Element ) o ). forEach ( Hibernate :: unproxy );
2638
2739 @ Inject
2840 private MetamodelProvider metamodelProvider ;
@@ -80,17 +92,34 @@ public Set<Element> findAllByCommit(Commit commit) {
8092 return jpa .transact (em -> {
8193 // TODO Commit is detached at this point. This ternary mitigates by requerying for the Commit in this transaction. A better solution would be moving transaction handling up to service layer (supported by general wisdom) and optionally migrating to using Play's @Transactional/JPAApi. Pros would include removal of repetitive transaction handling at the DAO layer and ability to interface with multiple DAOs in the same transaction (consistent view). Cons include increased temptation to keep transaction open for longer than needed, e.g. during JSON serialization due to the convenience of @Transactional (deprecated in >= 2.8.x), and the service, a higher level of abstraction, becoming aware of transactions. An alternative would be DAO-to-DAO calls (generally discouraged) and delegating to non-transactional versions of methods.
8294 Commit c = em .contains (commit ) ? commit : em .find (metamodelProvider .getImplementationClass (Commit .class ), commit .getId ());
83- return streamFlattenedElements ( c ).peek (PROXY_RESOLVER ).collect (Collectors .toSet ());
95+ return getCommitIndex ( c , em ). getWorkingElementVersions (). stream (). map ( ElementVersion :: getData ). filter ( mof -> mof instanceof Element ). map ( mof -> ( Element ) mof ).peek (PROXY_RESOLVER ).collect (Collectors .toSet ());
8496 });
8597 }
8698
8799 @ Override
88100 public Optional <Element > findByCommitAndId (Commit commit , UUID id ) {
89101 return jpa .transact (em -> {
90- return queryCommitTree (em .contains (commit ) ? commit : em .find (metamodelProvider .getImplementationClass (Commit .class ), commit .getId ()), c ->
91- c .getChanges ().stream ().filter (record -> record .getIdentity () != null && record .getIdentity ().getId () != null && record .getData () instanceof Element ).filter (record -> id .equals (record .getIdentity ().getId ())).map (record -> (Element ) record .getData ()).findAny (),
92- Optional ::isPresent )
93- .values ().stream ().filter (Optional ::isPresent ).map (Optional ::get ).peek (PROXY_RESOLVER ).findAny ();
102+ // TODO Commit is detached at this point. This ternary mitigates by requerying for the Commit in this transaction. A better solution would be moving transaction handling up to service layer (supported by general wisdom) and optionally migrating to using Play's @Transactional/JPAApi. Pros would include removal of repetitive transaction handling at the DAO layer and ability to interface with multiple DAOs in the same transaction (consistent view). Cons include increased temptation to keep transaction open for longer than needed, e.g. during JSON serialization due to the convenience of @Transactional (deprecated in >= 2.8.x), and the service, a higher level of abstraction, becoming aware of transactions. An alternative would be DAO-to-DAO calls (generally discouraged) and delegating to non-transactional versions of methods.
103+ Commit c = em .contains (commit ) ? commit : em .find (metamodelProvider .getImplementationClass (Commit .class ), commit .getId ());
104+ CommitIndex commitIndex = getCommitIndex (c , em );
105+
106+ CriteriaBuilder builder = em .getCriteriaBuilder ();
107+ CriteriaQuery <ElementVersionImpl > query = builder .createQuery (ElementVersionImpl .class );
108+ Root <CommitIndexImpl > commitIndexRoot = query .from (CommitIndexImpl .class );
109+ SetJoin <CommitIndexImpl , ElementVersionImpl > workingElementVersionsJoin = commitIndexRoot .join (CommitIndexImpl_ .workingElementVersions );
110+ Join <ElementVersionImpl , ElementIdentityImpl > elementIdentityJoin = workingElementVersionsJoin .join (ElementVersionImpl_ .identity );
111+ query .select (workingElementVersionsJoin ).where (
112+ builder .equal (commitIndexRoot .get (CommitIndexImpl_ .id ), commitIndex .getId ()),
113+ builder .equal (elementIdentityJoin .get (ElementIdentityImpl_ .id ), id )
114+ );
115+ try {
116+ return Optional .of (em .createQuery (query ).getSingleResult ()).map (ElementVersion ::getData ).filter (mof -> mof instanceof Element ).map (mof -> (Element ) mof ).map (element -> {
117+ PROXY_RESOLVER .accept (element );
118+ return element ;
119+ });
120+ } catch (NoResultException e ) {
121+ return Optional .empty ();
122+ }
94123 });
95124 }
96125
@@ -114,13 +143,37 @@ protected <T> Map<Commit, T> queryCommitTree(Commit commit, Function<Commit, T>
114143 return results ;
115144 }
116145
117- protected Stream <Element > streamFlattenedElements (Commit commit ) {
118- Set <UUID > visitedElements = new HashSet <> ();
119- Map <Commit , Stream <Element >> results = queryCommitTree (commit ,
120- c -> c .getChanges ().stream ().filter (record -> record .getIdentity () != null && record .getIdentity ().getId () != null && record .getData () instanceof Element ).filter (record -> !visitedElements .contains (record .getIdentity ().getId ())).peek (record -> visitedElements .add (record .getIdentity ().getId ())). map ( record -> ( Element ) record . getData ( )));
146+ protected Stream <ElementVersion > streamWorkingElementVersions (Commit commit ) {
147+ Set <UUID > visitedElements = ConcurrentHashMap . newKeySet ();
148+ Map <Commit , Stream <ElementVersion >> results = queryCommitTree (commit ,
149+ c -> c .getChanges ().stream ().filter (record -> record .getIdentity () != null && record .getIdentity ().getId () != null && record .getData () != null ).filter (record -> !visitedElements .contains (record .getIdentity ().getId ())).peek (record -> visitedElements .add (record .getIdentity ().getId ())));
121150 return results .values ().stream ().flatMap (Function .identity ());
122151 }
123152
153+ protected CommitIndex getCommitIndex (Commit commit , EntityManager em ) {
154+ CriteriaBuilder builder = em .getCriteriaBuilder ();
155+ CriteriaQuery <CommitIndexImpl > query = builder .createQuery (CommitIndexImpl .class );
156+ Root <CommitIndexImpl > root = query .from (CommitIndexImpl .class );
157+ query .select (root ).where (builder .equal (root .get (CommitIndexImpl_ .id ), commit .getId ()));
158+ CommitIndex commitIndex = null ;
159+ try {
160+ commitIndex = em .createQuery (query ).getSingleResult ();
161+ } catch (NoResultException ignored ) {
162+ }
163+ if (commitIndex != null ) {
164+ return commitIndex ;
165+ }
166+
167+ commitIndex = new CommitIndexImpl ();
168+ commitIndex .setCommit (commit );
169+ commitIndex .setWorkingElementVersions (streamWorkingElementVersions (commit ).collect (Collectors .toSet ()));
170+ EntityTransaction transaction = em .getTransaction ();
171+ transaction .begin ();
172+ em .persist (commitIndex );
173+ transaction .commit ();
174+ return commitIndex ;
175+ }
176+
124177 private Expression <Boolean > getTypeExpression (CriteriaBuilder builder , Root <?> root ) {
125178 return builder .or (metamodelProvider .getAllImplementationClasses ().stream ().filter (Element .class ::isAssignableFrom ).map (c -> builder .equal (root .type (), c )).toArray (Predicate []::new ));
126179 }
0 commit comments