Skip to content

Commit

Permalink
Improved JPA bootstrap.
Browse files Browse the repository at this point in the history
  • Loading branch information
nmihajlovski committed Apr 14, 2016
1 parent 0b8f15d commit 274c043
Show file tree
Hide file tree
Showing 6 changed files with 364 additions and 257 deletions.
265 changes: 265 additions & 0 deletions rapidoid-commons/src/main/java/org/rapidoid/jpa/EM.java
@@ -0,0 +1,265 @@
package org.rapidoid.jpa;

import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.beany.Beany;
import org.rapidoid.cls.Cls;
import org.rapidoid.u.U;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Parameter;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.metamodel.EntityType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/*
* #%L
* rapidoid-commons
* %%
* Copyright (C) 2014 - 2016 Nikolche Mihajlovski and contributors
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

@Authors("Nikolche Mihajlovski")
@Since("5.1.0")
public class EM {

private final EntityManager em;

public EM(EntityManager em) {
this.em = em;
}

public Object save(Object entity) {
Object id = Beany.getIdIfExists(entity);

if (id == null) {
return insert(entity);

} else {
update(entity);
return id;
}
}

public void update(Object entity) {
ensureNotInReadOnlyTransation();
em.persist(entity);
}

public <E> List<E> getAll(Class<E> clazz, List<String> ids) {
List<E> results = new ArrayList<E>();

for (Object id : ids) {
results.add(this.<E>get(clazz, id));
}

return results;
}

public <E> E get(Class<E> clazz, Object id) {
E entity = find(clazz, id);
U.must(entity != null, "Cannot find %s with ID=%s", clazz.getSimpleName(), id);
return entity;
}

public <E> E ref(Class<E> clazz, Object id) {
return em.getReference(clazz, id);
}

public Object insert(Object entity) {
ensureNotInReadOnlyTransation();

EntityTransaction tx = em.getTransaction();

boolean txWasActive = tx.isActive();

if (!txWasActive) {
tx.begin();
}

try {
em.persist(entity);
em.flush();

Object id = Beany.getId(entity);

if (!txWasActive) {
tx.commit();
}

return id;

} catch (Throwable e) {
if (!txWasActive) {
if (tx.isActive()) {
tx.rollback();
}
}
throw U.rte("Transaction execution error, rolled back!", e);
}
}

public <T> T find(Class<T> clazz, Object id) {
return em.find(clazz, id);
}

public List<EntityType<?>> getEntityTypes() {
return U.list(em.getMetamodel().getEntities());
}

@SuppressWarnings("unchecked")
public <E> List<E> getAll() {
List<E> all = U.list();

for (EntityType<?> entityType : getEntityTypes()) {
List<E> entities = (List<E>) getAll(entityType.getJavaType());
all.addAll(entities);
}

return all;
}

public <T> List<T> getAll(Class<T> clazz) {
CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaQuery<T> query = cb.createQuery(clazz);
CriteriaQuery<T> all = query.select(query.from(clazz));

return em.createQuery(all).getResultList();
}

public long count(Class<?> clazz) {
CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaQuery<Long> cq = cb.createQuery(Long.class);
cq.select(cb.count(cq.from(clazz)));

return em.createQuery(cq).getSingleResult();
}

public void refresh(Object entity) {
em.refresh(entity);
}

public void merge(Object entity) {
em.merge(entity);
}

public <E> void delete(Class<E> clazz, Object id) {
ensureNotInReadOnlyTransation();
em.remove(get(clazz, id));
}

public void delete(Object entity) {
ensureNotInReadOnlyTransation();
em.remove(entity);
}

public void transaction(Runnable action, boolean readOnly) {
final EntityTransaction tx = em.getTransaction();
U.notNull(tx, "transaction");

if (readOnly) {
runTxReadOnly(action, tx);
} else {
runTxRW(action, tx);
}
}

private void runTxReadOnly(Runnable action, EntityTransaction tx) {
boolean txWasActive = tx.isActive();

if (!txWasActive) {
tx.begin();
}

tx.setRollbackOnly();

try {
action.run();
} catch (Throwable e) {
tx.rollback();
throw U.rte("Transaction execution error, rolled back!", e);
}

if (!txWasActive) {
tx.rollback();
}
}

private void runTxRW(Runnable action, EntityTransaction tx) {
boolean txWasActive = tx.isActive();

if (!txWasActive) {
tx.begin();
}

try {
action.run();
} catch (Throwable e) {
tx.rollback();
throw U.rte("Transaction execution error, rolled back!", e);
}

if (!txWasActive) {
tx.commit();
}
}

private void ensureNotInReadOnlyTransation() {
EntityTransaction tx = em.getTransaction();
U.must(!tx.isActive() || !tx.getRollbackOnly(), "Cannot perform writes inside read-only transaction!");
}

public boolean isLoaded(Object entity) {
return em.getEntityManagerFactory().getPersistenceUnitUtil().isLoaded(entity);
}

public boolean isLoaded(Object entity, String attribute) {
return em.getEntityManagerFactory().getPersistenceUnitUtil().isLoaded(entity, attribute);
}

public Object getIdentifier(Object entity) {
return em.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(entity);
}

public <T> List<T> jpql(String jpql, Object... args) {
return jpql(jpql, null, args);
}

public <T> List<T> jpql(String jpql, Map<String, ?> namedArgs, Object... args) {
Query q = JPA.em().createQuery(jpql);

for (int i = 0; i < args.length; i++) {
q.setParameter(i + 1, args[i]);
}

for (Parameter<?> param : q.getParameters()) {
String name = param.getName();
if (U.notEmpty(name)) {
q.setParameter(name, Cls.convert(namedArgs.get(name), param.getParameterType()));
}
}

return q.getResultList();
}

}
78 changes: 24 additions & 54 deletions rapidoid-commons/src/main/java/org/rapidoid/jpa/JPA.java
Expand Up @@ -2,19 +2,14 @@

import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.cls.Cls;
import org.rapidoid.commons.Coll;
import org.rapidoid.ctx.Ctx;
import org.rapidoid.ctx.Ctxs;
import org.rapidoid.u.U;
import org.rapidoid.util.Msc;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.metamodel.EntityType;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/*
* #%L
Expand All @@ -40,22 +35,12 @@
@Since("5.1.0")
public class JPA {

private static volatile EntityManagerFactory emf;

private static final List<String> entities = U.list();

public static EntityManager em() {
Ctx ctx = Ctxs.get();
if (ctx != null) {
return (EntityManager) ctx.persister();
} else {
U.notNull(emf, "JPA.emf");
return emf.createEntityManager();
}
return JPAUtil.em();
}

public static JPAUtil with(EntityManager em) {
return JPAUtil.with(em);
public static EM with(EntityManager em) {
return new EM(em);
}

public static <E> E ref(Class<E> clazz, Object id) {
Expand All @@ -74,10 +59,6 @@ public static <E> List<E> getAllEntities() {
return with(em()).getAll();
}

public static List<EntityType<?>> getEntityTypes() {
return with(em()).getEntityTypes();
}

public static <E> List<E> getAll(Class<E> clazz, List<String> ids) {
return with(em()).getAll(clazz, ids);
}
Expand Down Expand Up @@ -150,35 +131,7 @@ public static void flush() {
}

public static void bootstrap(String[] path, Class<?>... providedEntities) {
if (Cls.exists("org.hibernate.cfg.Configuration") && entities.isEmpty()) {
Msc.logSection("Bootstrapping JPA (Hibernate)...");

List<String> entityTypes = EMFUtil.createEMF(path, providedEntities);

if (entityTypes.isEmpty()) {
Msc.logSection("Didn't find JPA providedEntities, canceling JPA/Hibernate setup!");
}

Msc.logSection("Hibernate properties:");
Properties props = EMFUtil.hibernateProperties();
Msc.logProperties(props);

Msc.logSection("Starting Hibernate:");

CustomHibernatePersistenceProvider provider = new CustomHibernatePersistenceProvider();
provider.names().addAll(entityTypes);

EntityManagerFactory emf = provider.createEntityManagerFactory("pu", props);
JPA.emf(emf);

Msc.logSection("JPA (Hibernate) is ready.");

Coll.assign(entities, entityTypes);
}
}

public static List<String> entities() {
return entities;
JPAUtil.bootstrap(path, providedEntities);
}

public static boolean isLoaded(Object entity) {
Expand All @@ -202,14 +155,31 @@ public static <T> List<T> jpql(String jpql, Map<String, ?> namedArgs, Object...
}

public static boolean isEntity(Object obj) {
return obj != null && entities().contains(obj.getClass().getName());
return JPAUtil.isEntity(obj);
}

public static List<EntityType<?>> getEntityTypes() {
return with(em()).getEntityTypes();
}

public static List<String> entities() {
return JPAUtil.entities;
}

public static List<Class<?>> getEntityJavaTypes() {
return JPAUtil.entityJavaTypes;
}

public static EntityManagerFactory emf() {
return emf;
return JPAUtil.emf;
}

public static void emf(EntityManagerFactory emf) {
JPA.emf = emf;
JPAUtil.emf(emf);
}

public static <T> T unproxy(T entity) {
return JPAUtil.unproxy(entity);
}

}

0 comments on commit 274c043

Please sign in to comment.