Home » Eclipse Projects » Eclipse Scout » Scout + JPA. Where is good place to get EntityManager?
| |
Re: Scout + JPA. Where is good place to get EntityManager? [message #1828649 is a reply to message #1828477] |
Mon, 15 June 2020 18:57 |
Darth Bolek Messages: 25 Registered: August 2019 |
Junior Member |
|
|
Thank you for input, but
I do not think this would work well in JPA world.
In JDBC world, to make it usable, the Database1.class would have to include connection pool. java.sql.Connection in theory should be thread safe, but in practice it depends on driver implementation. Even if it is thread safe, it may be through use of "synchronize" - and that implies performance bottleneck.
Instead of relying on the quality of the JDBC driver, I'd rather avoid the issue at all by using connection pool.
XA transactions are out of scope for me right now, but thank you for pointing me to scout jdbc package - I wasn't aware of its existence, and I am going to use it at some point.
I still think unique EM per each session is good idea. After some testing and session juggling (there is client, server and UiSession), it looks like staying at server side in ServerSession class is the pragmatic solution. I really like the concept of IGlobalSessionListener interface, but that was working only at the client (scout client) side. And since EM is internal object to server side, behind JPA, behind entity repository, creating this at the client side does not feel right.
So, what I have right now:
* all at server side
* EM created, cached and removed per each session
* entity repository/DAO using the session EM
* ScoutRole is the JPA entity
What I don't like about it, is instantiating repository every time it is used. Since each session has to have its own, it cannot really be @ApplicationScoped. Cache it? - it is quickly becoming application of caches... cache of user caches from another my thread...
For the client part I do not want it to be aware of any technical details from the server side, so connections, transactions, statements, entity managers are out of question. It is likely I will use another layer of business level repository, one per each client UI use case - but I am not there yet...
Here is the code:
package org.eclipse.scout.apps.helloworld.server;
...
public class ServerSession extends AbstractServerSession {
private static final long serialVersionUID = 1L;
private static final XLogger LOG = XLoggerFactory.getXLogger(ServerSession.class);
public static String JPA_EM_CACHE_ID = ServerSession.class.getName() + ".JPA_EM_CACHE_ID";
public ServerSession() {
super(true);
}
/**
* @return The {@link ServerSession} which is associated with the current
* thread, or {@code null} if not found.
*/
public static ServerSession get() {
return ServerSessionProvider.currentSession(ServerSession.class);
}
@Override
protected void execLoadSession() {
LOG.entry();
LOG.info("created a new session for {}", getUserId());
// SERVICES.getService(IAccessControlService.class).clearCacheOfUserIds(Collections.singleton(getUserId()));
// add EM to cache per session
String sessionId = getId();
LOG.debug("sessionId: {}", sessionId);
ISession s = ISession.CURRENT.get();
LOG.debug("session: {}", s);
LOG.debug("sessionId from ISession: {}", (s!=null) ? s.getId() : s);
getEMCache().get( new KeyContext(sessionId) );
LOG.exit();
}
@Override
public void stop() {
LOG.entry();
// remove EM from cache
String sessionId = getId();
LOG.debug("sessionId: {}", sessionId);
KeyContext key = new KeyContext(sessionId);
getEMCache().get(key).close();
KeyCacheEntryFilter<KeyContext, EntityManager> filter = new KeyCacheEntryFilter<KeyContext, EntityManager>(Collections.singletonList(key));
getEMCache().invalidate(filter, true);
super.stop();
LOG.exit();
}
private ICache<KeyContext, EntityManager> getEMCache() {
LOG.entry();
ICache<KeyContext, EntityManager> result = null;
CacheRegistryService crs = BEANS.get(CacheRegistryService.class);
result = crs.opt(JPA_EM_CACHE_ID);
if (result == null) {
result = createEMCache();
}
return LOG.exit(result);
}
private ICache<KeyContext, EntityManager> createEMCache() {
LOG.entry();
ICache<KeyContext, EntityManager> result = null;
@SuppressWarnings("unchecked")
ICacheBuilder<KeyContext, EntityManager> cacheBuilder = BEANS.get(ICacheBuilder.class);
result = cacheBuilder
.withCacheId(JPA_EM_CACHE_ID)
.withValueResolver(new EMCacheValueResolver())
.withThreadSafe(true)
.build();
return LOG.exit(result);
}
}
package org.eclipse.scout.apps.helloworld.server;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.eclipse.scout.rt.platform.cache.ICacheValueResolver;
import org.slf4j.ext.XLogger;
import org.slf4j.ext.XLoggerFactory;
import io.poc.scout.ldap.security.cache.KeyContext;
public class EMCacheValueResolver implements ICacheValueResolver<KeyContext, EntityManager> {
private static final XLogger LOG = XLoggerFactory.getXLogger(EMCacheValueResolver.class);
private static final String PERSISTENCE_UNIT_NAME = "scout-roles";
private static EntityManagerFactory factory = null;
public EMCacheValueResolver() {
LOG.entry();
if (EMCacheValueResolver.factory == null) {
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
}
LOG.exit();
}
@Override
public EntityManager resolve(KeyContext key) {
LOG.entry(key);
EntityManager result = null;
result = factory.createEntityManager();
return LOG.exit(result);
}
}
import org.eclipse.scout.rt.shared.session.IGlobalSessionListener;
import org.eclipse.scout.rt.shared.session.SessionEvent;
import org.slf4j.ext.XLogger;
import org.slf4j.ext.XLoggerFactory;
public class MySessionListener implements IGlobalSessionListener {
private static final XLogger LOG = XLoggerFactory.getXLogger(MySessionListener.class);
public MySessionListener() {
LOG.entry();
LOG.exit();
}
@Override
public void sessionChanged(SessionEvent event) {
LOG.entry(event);
LOG.debug("event type: {} id: {}", event.getType(), event.getSource().getId());
switch ( event.getType() ) {
case SessionEvent.TYPE_STARTED : {
LOG.debug("session started");
break;
}
case SessionEvent.TYPE_STOPPING : {
LOG.debug("session is stopping");
break;
}
case SessionEvent.TYPE_STOPPED :
default :
// do nothing
} // switch
LOG.exit();
}
}
public interface IScoutRepository<E> {
E persist(E entity);
void remove(E entity);
<K> E findById(K id);
Set<E> findAll();
E findByName(String name);
}
public abstract class JPARepository<E> implements IScoutRepository<E> {
private static final XLogger LOG = XLoggerFactory.getXLogger(JPARepository.class);
protected Class<?> entityClass;
protected EntityManager em = null;
public JPARepository() {
LOG.entry();
final ParameterizedType genericSuperclass = (ParameterizedType) this.getClass().getGenericSuperclass();
LOG.debug("genericSuperclass: {}", genericSuperclass);
this.entityClass = (Class<?>) genericSuperclass.getActualTypeArguments()[0];
LOG.debug("entityClass: {}", entityClass);
ServerSession s = ServerSessionProvider.currentSession(ServerSession.class);
LOG.debug("sessionId: {}", s.getId());
String userId = s.getUserId();
LOG.debug("userId from session: {}", userId);
// get EM from cache
em = getEMCache().get(new KeyContext(s.getId() ) );
LOG.exit();
}
@Override
public E persist(E entity) {
LOG.entry(entity);
E result = null;
em.getTransaction().begin();
em.persist(entity);
em.getTransaction().commit();
result = entity;
return LOG.exit(result);
}
@Override
public void remove(E entity) {
LOG.entry(entity);
em.getTransaction().begin();
em.persist(entity);
em.getTransaction().commit();
LOG.exit();
}
@SuppressWarnings("unchecked")
@Override
public <K> E findById(K id) {
LOG.entry(id);
E result = null;
em.getTransaction().begin();
result = (E) em.find(entityClass, id);
em.getTransaction().commit();
return LOG.exit(result);
}
@SuppressWarnings("unchecked")
@Override
public Set<E> findAll() {
LOG.entry();
Set<E> result = null;
em.getTransaction().begin();
Query q = em.createQuery("SELECT e FROM " + entityClass.getName() + " e").setHint("org.hibernate.readOnly", true);
result = (Set<E>) q.getResultStream().collect(Collectors.toSet());
em.getTransaction().commit();
return LOG.exit(result);
}
@Override
public abstract E findByName(String name);
protected ICache<KeyContext, EntityManager> getEMCache() {
LOG.entry();
ICache<KeyContext, EntityManager> result = null;
CacheRegistryService crs = BEANS.get(CacheRegistryService.class);
result = crs.get(ServerSession.JPA_EM_CACHE_ID);
return LOG.exit(result);
}
}
public interface IScoutRoleRepository<ScoutRole> extends IScoutRepository<ScoutRole> {
Collection<ScoutRole> findByName(Collection<String> roles);
}
public class ScoutRoleRepository extends JPARepository<ScoutRole> implements IScoutRoleRepository<ScoutRole> {
private static final XLogger LOG = XLoggerFactory.getXLogger(ScoutRoleRepository.class);
public ScoutRoleRepository() {
super();
ServerSession s = ServerSessionProvider.currentSession(ServerSession.class);
LOG.debug("sessionId: {}", s.getId());
String userId = s.getUserId();
LOG.debug("userId from session: {}", userId);
LOG.exit();
}
@Override
public ScoutRole findByName(String name) {
LOG.entry(name);
ScoutRole result = null;
em.getTransaction().begin();
Query q = em.createNativeQuery("SELECT A.ID, A.NAME, A.VER FROM SC_ROLE A WHERE A.NAME = ?", ScoutRole.class);
q.setParameter(1, name);
result = (ScoutRole) q.getSingleResult();
em.getTransaction().commit();
return LOG.exit(result);
}
@Override
public Collection<ScoutRole> findByName(Collection<String> roles) {
LOG.entry(roles);
Collection<ScoutRole> result = null;
// TODO
return LOG.exit(result);
}
}
How to use it:
IScoutRepository<ScoutRole> repo = new ScoutRoleRepository();
ScoutRole tmpRole = repo.findByName(name);
// with cache
for (String role : roles) {
KeyContext key = new KeyContext(role);
key.setCtx(repo);
ScoutRole sr = cacheRoles.get(key);
result.put(role, sr);
}
|
|
|
Goto Forum:
Current Time: Tue Nov 12 03:22:26 GMT 2024
Powered by FUDForum. Page generated in 0.02477 seconds
|