Monday 13 April 2020

Flowable UI local development

Flowable provides 4 UI applications, take modeler for instance, in order to develop locally:

  1. Create local dir, say my-ui-idm, and copy all contents in flowable-ui-idm into it, as all other UI app needs this idm application, so we have to setup idm first
  2. Create local dir, say my-ui-modeler, and copy all contens in flowable-ui-modeler into it.
  3. Create local dir to hold flowable-spring-boot project.
  4. Modify pom.xml, normally we use a stable version not a SNAPSHOT version, for instance, change all <version> in <parent> to a stable version, for instance, <version>6.5.0</version>, this change will apply to the above three projects
  5. in 1 and 2 projects, you may also need to change <relativePath> value in pom.xml relative to the flowable-spring-boot project
  6. modify flowable-default.properties file if you need, for instance, you want to use a different datasource. This file in both flowable-ui-idm-app/resources and flowable-ui-modeler-app/resources 
  7. run flowable-ui-idm application 
  8. run flowable-ui-modeler application
  9. access url, for instance, http://localhost:8888/flowable-modeler, you may redirect to login page if you first access, and after input user "admin" and password "test" (these all configed in the properties file), you will see modeler process list page.

Thursday 2 April 2020

how to make native sql update visible to jpa query

1. Background
Native update then query using jpa (spring data jpa, hibernate as the persistence provider) cannot see native sql's updates in a same transaction.


JPA (native) update jdbc native update
jpa query 1 not visible 2 not visible
jdbc query 3 visible 4 visible

The objective is to solve 1 and 2 to make sure JPA update (via native sql) and jdbc native sql update result are visible to following JAP query.

2. Solution
2.1 case by case
If you are going to solve the issue temporarily, and you can access the entityManager, then the em.refresh(entity) may solve your problem. For instance, We have a DemoRepository where:
@Modifying()
@Query(value = "update demo set name=:newName where id=:id", nativeQuery = true)
void updateNameNative(@Param("newName") String newName, @Param("id")int id);

// you have saved an demo instance with name of "name3", then do an update as below
demoRepository.updateNameNative("name3-1", demo1.getId());
Optional<Demo> demoFromJpa1 = demoRepository.findById(demo1.getId());
em.refresh(demoFromJpa1.get());
if (demoFromJpa1.isPresent()) System.out.println("1. jpa native update name to 'name3-1', then jpa query: " + demoFromJpa1.get());

If you don't refresh, the result will be "name3", if you do, you will see "name3-1"

2.2 Using Spring data annotation
For the above update method, if you do not want to call low level em.refresh method, you can add below attributes into @Modify annotation, to let spring data help to clear the persistence context thus changes can be seen after native sql updates.

@Modifying(flushAutomatically = true, clearAutomatically=true)
@Query(value = "update demo set name=:newName where id=:id", nativeQuery = true)
void updateNameNative(@Param("newName") String newName, @Param("id")int id);

2.3 AOP way

Create an Aspect to intercept all ( or part of ) Spring data jpa (or whatever you need) methods to flush changes to db before query, and clear persistence context after query, then you will get latest updates. Actually this is what the above @Modifying annotation's two attributes works.

@Aspect@Component@Configurablepublic class EntityMangerAspect {
    @PersistenceContext    private EntityManager em;

    @Pointcut("execution(* org.springframework.data.jpa.repository.JpaRepository+.*(*))")
    public void allMethodInJpaRepository() {
    }

    @Around("allMethodInJpaRepository()")
    public Object beforeAllMethodInJpaRepository(ProceedingJoinPoint pjp) {
        Object result = null;
        try {
            em.flush();
            result = pjp.proceed();
            em.clear();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }
}

FYI: JPA query in Spring data jpa support the above two attributes
this.em = em;
this.flush = method.getFlushAutomatically();
this.clear = method.getClearAutomatically();

   @Override   protected Object doExecute(AbstractJpaQuery query, Object[] values) {

      if (flush) {
         em.flush();
      }

      int result = query.createQuery(values).executeUpdate();

      if (clear) {
         em.clear();
      }

      return result;
   }
}