11package org .zstack .core .db ;
22
3+ import com .google .common .collect .Maps ;
4+ import java .util .concurrent .CopyOnWriteArrayList ;
35import org .hibernate .exception .ConstraintViolationException ;
46import org .springframework .beans .factory .annotation .Autowired ;
57import org .springframework .core .NestedExceptionUtils ;
@@ -66,7 +68,7 @@ class EntityInfo {
6668 Field eoSoftDeleteColumn ;
6769 Class eoClass ;
6870 Class voClass ;
69- Map <EntityEvent , EntityLifeCycleCallback > listeners = new HashMap < EntityEvent , EntityLifeCycleCallback > ();
71+ Map <EntityEvent , List < EntityLifeCycleCallback >> listeners = Maps . newConcurrentMap ();
7072
7173 EntityInfo (Class voClazz ) {
7274 voClass = voClazz ;
@@ -82,12 +84,16 @@ class EntityInfo {
8284 EO at = (EO ) voClazz .getAnnotation (EO .class );
8385 if (at != null ) {
8486 eoClass = at .EOClazz ();
85- DebugUtils .Assert (eoClass != null , String .format ("cannot find EO entity specified by VO entity[%s]" , voClazz .getName ()));
87+ DebugUtils .Assert (eoClass != null ,
88+ String .format ("cannot find EO entity specified by VO entity[%s]" , voClazz .getName ()));
8689 eoPrimaryKeyField = FieldUtils .getAnnotatedField (Id .class , eoClass );
87- DebugUtils .Assert (eoPrimaryKeyField != null , String .format ("cannot find primary key field(@Id annotated) in EO entity[%s]" , eoClass .getName ()));
90+ DebugUtils .Assert (eoPrimaryKeyField != null , String
91+ .format ("cannot find primary key field(@Id annotated) in EO entity[%s]" , eoClass .getName ()));
8892 eoPrimaryKeyField .setAccessible (true );
8993 eoSoftDeleteColumn = FieldUtils .getField (at .softDeletedColumn (), eoClass );
90- DebugUtils .Assert (eoSoftDeleteColumn != null , String .format ("cannot find soft delete column[%s] in EO entity[%s]" , at .softDeletedColumn (), eoClass .getName ()));
94+ DebugUtils .Assert (eoSoftDeleteColumn != null ,
95+ String .format ("cannot find soft delete column[%s] in EO entity[%s]" , at .softDeletedColumn (),
96+ eoClass .getName ()));
9197 eoSoftDeleteColumn .setAccessible (true );
9298 }
9399
@@ -108,7 +114,8 @@ private void buildSoftDeletionCascade() {
108114 for (final SoftDeletionCascade at : ats .value ()) {
109115 final Class parent = at .parent ();
110116 if (!parent .isAnnotationPresent (Entity .class )) {
111- throw new CloudRuntimeException (String .format ("class[%s] has annotation @SoftDeletionCascade but its parent class[%s] is not annotated by @Entity" ,
117+ throw new CloudRuntimeException (String .format (
118+ "class[%s] has annotation @SoftDeletionCascade but its parent class[%s] is not annotated by @Entity" ,
112119 voClass , parent ));
113120 }
114121
@@ -131,7 +138,8 @@ public List<Class> getEntityClassForSoftDeleteEntityExtension() {
131138 @ Override
132139 @ Transactional
133140 public void postSoftDelete (Collection entityIds , Class entityClass ) {
134- String sql = String .format ("delete from %s me where me.%s in (:ids)" , voClass .getSimpleName (), at .joinColumn ());
141+ String sql = String .format ("delete from %s me where me.%s in (:ids)" , voClass .getSimpleName (),
142+ at .joinColumn ());
135143 Query q = getEntityManager ().createQuery (sql );
136144 q .setParameter ("ids" , entityIds );
137145 q .executeUpdate ();
@@ -148,7 +156,8 @@ private void buildInheritanceDeletionExtension() {
148156
149157 final Class parent = voClass .getSuperclass ();
150158 if (!parent .isAnnotationPresent (Entity .class )) {
151- throw new CloudRuntimeException (String .format ("class[%s] has annotation @PrimaryKeyJoinColumn but its parent class[%s] is not annotated by @Entity" ,
159+ throw new CloudRuntimeException (String .format (
160+ "class[%s] has annotation @PrimaryKeyJoinColumn but its parent class[%s] is not annotated by @Entity" ,
152161 voClass , parent ));
153162 }
154163
@@ -245,17 +254,21 @@ private void updateEO(Object entity, RuntimeException de) {
245254 }
246255
247256 SQLIntegrityConstraintViolationException me = (SQLIntegrityConstraintViolationException ) rootCause ;
248- if (!(me .getErrorCode () == 1062 && "23000" .equals (me .getSQLState ()) && me .getMessage ().contains ("PRIMARY" ))) {
257+ if (!(me .getErrorCode () == 1062 && "23000" .equals (me .getSQLState ())
258+ && me .getMessage ().contains ("PRIMARY" ))) {
249259 throw de ;
250260 }
251261
252262 if (!hasEO ()) {
253263 throw de ;
254264 }
255265
256- // at this point, the error is caused by a update tried on VO entity which has been soft deleted. This is mostly
257- // caused by a deletion cascade(e.g deleting host will cause vm running on it to be deleted, and deleting vm is trying to return capacity
258- // to host which has been soft deleted, because vm deletion is executed in async manner). In this case, we make the update to EO table
266+ // at this point, the error is caused by a update tried on VO entity which has
267+ // been soft deleted. This is mostly
268+ // caused by a deletion cascade(e.g deleting host will cause vm running on it to
269+ // be deleted, and deleting vm is trying to return capacity
270+ // to host which has been soft deleted, because vm deletion is executed in async
271+ // manner). In this case, we make the update to EO table
259272
260273 Object idval = getEOPrimaryKeyValue (entity );
261274 Object eo = getEntityManager ().find (eoClass , idval );
@@ -360,8 +373,10 @@ private void hardDelete(Collection ids) {
360373
361374 @ Transactional
362375 private void nativeSqlDelete (Collection ids ) {
363- // native sql can avoid JPA cascades a deletion to parent entity when deleting a child entity
364- String sql = String .format ("delete from %s where %s in (:ids)" , voClass .getSimpleName (), voPrimaryKeyField .getName ());
376+ // native sql can avoid JPA cascades a deletion to parent entity when deleting a
377+ // child entity
378+ String sql = String .format ("delete from %s where %s in (:ids)" , voClass .getSimpleName (),
379+ voPrimaryKeyField .getName ());
365380 Query q = getEntityManager ().createNativeQuery (sql );
366381 q .setParameter ("ids" , ids );
367382 q .executeUpdate ();
@@ -418,7 +433,8 @@ List listByPrimaryKeys(Collection ids, int offset, int length) {
418433 sql = String .format ("select e from %s e" , voClass .getSimpleName ());
419434 query = getEntityManager ().createQuery (sql , voClass );
420435 } else {
421- sql = String .format ("select e from %s e where e.%s in (:ids)" , voClass .getSimpleName (), voPrimaryKeyField .getName ());
436+ sql = String .format ("select e from %s e where e.%s in (:ids)" , voClass .getSimpleName (),
437+ voPrimaryKeyField .getName ());
422438 query = getEntityManager ().createQuery (sql , voClass );
423439 query .setParameter ("ids" , ids );
424440 }
@@ -429,7 +445,8 @@ List listByPrimaryKeys(Collection ids, int offset, int length) {
429445
430446 @ Transactional (readOnly = true , propagation = Propagation .REQUIRES_NEW )
431447 boolean isExist (Object id ) {
432- String sql = String .format ("select count(*) from %s ref where ref.%s = :id" , voClass .getSimpleName (), voPrimaryKeyField .getName ());
448+ String sql = String .format ("select count(*) from %s ref where ref.%s = :id" , voClass .getSimpleName (),
449+ voPrimaryKeyField .getName ());
433450 TypedQuery <Long > q = getEntityManager ().createQuery (sql , Long .class );
434451 q .setParameter ("id" , id );
435452 q .setMaxResults (1 );
@@ -438,13 +455,25 @@ boolean isExist(Object id) {
438455 }
439456
440457 void installLifeCycleCallback (EntityEvent evt , EntityLifeCycleCallback l ) {
441- listeners .put (evt , l );
458+ List <EntityLifeCycleCallback > cbs = listeners .computeIfAbsent (evt , k -> new CopyOnWriteArrayList <>());
459+ if (!cbs .contains (l )) {
460+ cbs .add (l );
461+ }
462+ }
463+
464+ void uninstallLifeCycleCallback (EntityEvent evt , EntityLifeCycleCallback l ) {
465+ List <EntityLifeCycleCallback > cbs = listeners .get (evt );
466+ if (cbs != null ) {
467+ cbs .remove (l );
468+ }
442469 }
443470
444471 void fireLifeCycleEvent (EntityEvent evt , Object o ) {
445- EntityLifeCycleCallback cb = listeners .get (evt );
446- if (cb != null ) {
447- cb .entityLifeCycleEvent (evt , o );
472+ List <EntityLifeCycleCallback > cbs = listeners .get (evt );
473+ if (cbs != null ) {
474+ for (EntityLifeCycleCallback cb : cbs ) {
475+ cb .entityLifeCycleEvent (evt , o );
476+ }
448477 }
449478 }
450479 }
@@ -491,7 +520,8 @@ public CriteriaBuilder getCriteriaBuilder() {
491520
492521 @ Override
493522 public <T > SimpleQuery <T > createQuery (Class <T > entityClass ) {
494- assert entityClass .isAnnotationPresent (Entity .class ) : entityClass .getName () + " is not annotated by JPA @Entity" ;
523+ assert entityClass .isAnnotationPresent (Entity .class )
524+ : entityClass .getName () + " is not annotated by JPA @Entity" ;
495525 return new SimpleQueryImpl <T >(entityClass );
496526 }
497527
@@ -530,7 +560,6 @@ public void removeByPrimaryKeys(Collection priKeys, Class entityClazz) {
530560 getEntityInfo (entityClazz ).removeByPrimaryKeys (priKeys );
531561 }
532562
533-
534563 @ Override
535564 public <T > T updateAndRefresh (T entity ) {
536565 return (T ) getEntityInfo (entity .getClass ()).updateAndRefresh (entity );
@@ -636,7 +665,9 @@ public void entityForTranscationCallback(Operation op, Class<?>... entityClass)
636665 sb .append (c .getName ()).append ("," );
637666 }
638667
639- String err = String .format ("entityForTranscationCallback is called but transcation is not active. Did you forget adding @Transactional to method??? [operation: %s, entity classes: %s]" , op , sb .toString ());
668+ String err = String .format (
669+ "entityForTranscationCallback is called but transcation is not active. Did you forget adding @Transactional to method??? [operation: %s, entity classes: %s]" ,
670+ op , sb .toString ());
640671 logger .warn (err );
641672 }
642673 }
@@ -668,7 +699,8 @@ public long generateSequenceNumber(Class<?> seqTable) {
668699 try {
669700 Field id = seqTable .getDeclaredField ("id" );
670701 if (id == null ) {
671- throw new CloudRuntimeException (String .format ("sequence VO[%s] must have 'id' field" , seqTable .getName ()));
702+ throw new CloudRuntimeException (
703+ String .format ("sequence VO[%s] must have 'id' field" , seqTable .getName ()));
672704 }
673705 Object vo = seqTable .newInstance ();
674706 vo = persistAndRefresh (vo );
@@ -770,7 +802,7 @@ public void eoCleanup(Class VOClazz) {
770802 @ Override
771803 @ DeadlockAutoRestart
772804 public void eoCleanup (Class VOClazz , Object id ) {
773- if (id == null ) {
805+ if (id == null ) {
774806 throw new RuntimeException (String .format ("Cleanup %s EO fail, id is null" , VOClazz .getSimpleName ()));
775807 }
776808
@@ -798,7 +830,7 @@ public boolean start() {
798830 }
799831
800832 private void buildEntityInfo () {
801- BeanUtils .reflections .getTypesAnnotatedWith (Entity .class ).forEach (clz -> {
833+ BeanUtils .reflections .getTypesAnnotatedWith (Entity .class ).forEach (clz -> {
802834 entityInfoMap .put (clz , new EntityInfo (clz ));
803835 });
804836 }
@@ -820,7 +852,8 @@ private void populateExtensions() {
820852 }
821853 }
822854
823- for (SoftDeleteEntityByEOExtensionPoint ext : pluginRgty .getExtensionList (SoftDeleteEntityByEOExtensionPoint .class )) {
855+ for (SoftDeleteEntityByEOExtensionPoint ext : pluginRgty
856+ .getExtensionList (SoftDeleteEntityByEOExtensionPoint .class )) {
824857 for (Class eoClass : ext .getEOClassForSoftDeleteEntityExtension ()) {
825858 List <SoftDeleteEntityByEOExtensionPoint > exts = softDeleteByEOExtensions .get (eoClass );
826859 if (exts == null ) {
@@ -873,6 +906,20 @@ public void installEntityLifeCycleCallback(Class clz, EntityEvent evt, EntityLif
873906 }
874907 }
875908
909+ @ Override
910+ public void uninstallEntityLifeCycleCallback (Class clz , EntityEvent evt , EntityLifeCycleCallback cb ) {
911+ if (clz != null ) {
912+ EntityInfo info = entityInfoMap .get (clz );
913+ if (info != null ) {
914+ info .uninstallLifeCycleCallback (evt , cb );
915+ }
916+ } else {
917+ for (EntityInfo info : entityInfoMap .values ()) {
918+ info .uninstallLifeCycleCallback (evt , cb );
919+ }
920+ }
921+ }
922+
876923 @ Override
877924 public boolean stop () {
878925 return true ;
@@ -881,7 +928,8 @@ public boolean stop() {
881928 void entityEvent (EntityEvent evt , Object entity ) {
882929 EntityInfo info = entityInfoMap .get (entity .getClass ());
883930 if (info == null ) {
884- logger .warn (String .format ("cannot find EntityInfo for the class[%s], not entity events will be fired" , entity .getClass ()));
931+ logger .warn (String .format ("cannot find EntityInfo for the class[%s], not entity events will be fired" ,
932+ entity .getClass ()));
885933 return ;
886934 }
887935
0 commit comments