diff --git a/solon-plugin-data-sql/hibernate-solon-plugin/src/main/java/org/hibernate/solon/integration/EntityManagerProxy.java b/solon-plugin-data-sql/hibernate-solon-plugin/src/main/java/org/hibernate/solon/integration/EntityManagerProxy.java new file mode 100644 index 0000000000000000000000000000000000000000..4da9a1d28c87e72e950d62a11169fca21dd123b5 --- /dev/null +++ b/solon-plugin-data-sql/hibernate-solon-plugin/src/main/java/org/hibernate/solon/integration/EntityManagerProxy.java @@ -0,0 +1,346 @@ +package org.hibernate.solon.integration; + +import org.noear.solon.data.tran.TranListener; +import org.noear.solon.data.tran.TranUtils; + +import javax.persistence.*; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaDelete; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.CriteriaUpdate; +import javax.persistence.metamodel.Metamodel; +import java.util.List; +import java.util.Map; + +/** + * @author vanlin + * @className EntityManagerProxy + * @description + * @since 2024/7/5 15:16 + */ +public class EntityManagerProxy implements EntityManager { + private final EntityManager entityManager; + + public EntityManagerProxy(final EntityManager entityManager) { + this.entityManager = entityManager; + } + + private T tranTry(T entityManager){ + if(TranUtils.inTrans()){ + EntityTransaction transaction = entityManager.getTransaction(); + + if(!transaction.isActive()) { + transaction.begin(); + + TranUtils.listen(new TranListener() { + @Override + public void beforeCommit(boolean readOnly) throws Throwable { + if (readOnly) { + transaction.setRollbackOnly(); + } + transaction.commit(); + } + + @Override + public void afterCompletion(int status) { + if (status == TranListener.STATUS_ROLLED_BACK) { + transaction.rollback(); + } + } + }); + } + } + + return entityManager; + } + + @Override + public void persist(Object entity) { + tranTry(entityManager); + entityManager.persist(entity); + } + + @Override + public T merge(T entity) { + tranTry(entityManager); + return entityManager.merge(entity); + } + + @Override + public void remove(Object entity) { + tranTry(entityManager); + entityManager.remove(entity); + } + + @Override + public T find(Class entityClass, Object primaryKey) { + tranTry(entityManager); + return entityManager.find(entityClass, primaryKey); + } + + @Override + public T find(Class entityClass, Object primaryKey, Map properties) { + tranTry(entityManager); + return entityManager.find(entityClass, primaryKey, properties); + } + + @Override + public T find(Class entityClass, Object primaryKey, LockModeType lockMode) { + tranTry(entityManager); + return entityManager.find(entityClass, primaryKey, lockMode); + } + + @Override + public T find(Class entityClass, Object primaryKey, LockModeType lockMode, Map properties) { + tranTry(entityManager); + return entityManager.find(entityClass, primaryKey, lockMode, properties); + } + + @Override + public T getReference(Class entityClass, Object primaryKey) { + tranTry(entityManager); + return entityManager.getReference(entityClass, primaryKey); + } + + @Override + public void flush() { + tranTry(entityManager); + entityManager.flush(); + } + + @Override + public void setFlushMode(FlushModeType flushMode) { + entityManager.setFlushMode(flushMode); + } + + @Override + public FlushModeType getFlushMode() { + return entityManager.getFlushMode(); + } + + @Override + public void lock(Object entity, LockModeType lockMode) { + tranTry(entityManager); + entityManager.lock(entity, lockMode); + } + + @Override + public void lock(Object entity, LockModeType lockMode, Map properties) { + tranTry(entityManager); + entityManager.lock(entity, lockMode, properties); + } + + @Override + public void refresh(Object entity) { + tranTry(entityManager); + entityManager.refresh(entity); + } + + @Override + public void refresh(Object entity, Map properties) { + tranTry(entityManager); + entityManager.refresh(entity, properties); + } + + @Override + public void refresh(Object entity, LockModeType lockMode) { + tranTry(entityManager); + entityManager.refresh(entity, lockMode); + } + + @Override + public void refresh(Object entity, LockModeType lockMode, Map properties) { + tranTry(entityManager); + entityManager.refresh(entity, lockMode, properties); + } + + @Override + public void clear() { + tranTry(entityManager); + entityManager.clear(); + } + + @Override + public void detach(Object entity) { + tranTry(entityManager); + entityManager.detach(entity); + } + + @Override + public boolean contains(Object entity) { + tranTry(entityManager); + return entityManager.contains(entity); + } + + @Override + public LockModeType getLockMode(Object entity) { + tranTry(entityManager); + return entityManager.getLockMode(entity); + } + + @Override + public void setProperty(String propertyName, Object value) { + tranTry(entityManager); + entityManager.setProperty(propertyName, value); + } + + @Override + public Map getProperties() { + tranTry(entityManager); + return entityManager.getProperties(); + } + + @Override + public Query createQuery(String qlString) { + tranTry(entityManager); + return entityManager.createQuery(qlString); + } + + @Override + public TypedQuery createQuery(CriteriaQuery criteriaQuery) { + tranTry(entityManager); + return entityManager.createQuery(criteriaQuery); + } + + @Override + public Query createQuery(CriteriaUpdate updateQuery) { + tranTry(entityManager); + return entityManager.createQuery(updateQuery); + } + + @Override + public Query createQuery(CriteriaDelete deleteQuery) { + tranTry(entityManager); + return entityManager.createQuery(deleteQuery); + } + + @Override + public TypedQuery createQuery(String qlString, Class resultClass) { + tranTry(entityManager); + return entityManager.createQuery(qlString, resultClass); + } + + @Override + public Query createNamedQuery(String name) { + tranTry(entityManager); + return entityManager.createNamedQuery(name); + } + + @Override + public TypedQuery createNamedQuery(String name, Class resultClass) { + tranTry(entityManager); + return entityManager.createNamedQuery(name, resultClass); + } + + @Override + public Query createNativeQuery(String sqlString) { + tranTry(entityManager); + return entityManager.createNativeQuery(sqlString); + } + + @Override + public Query createNativeQuery(String sqlString, Class resultClass) { + tranTry(entityManager); + return entityManager.createNativeQuery(sqlString, resultClass); + } + + @Override + public Query createNativeQuery(String sqlString, String resultSetMapping) { + tranTry(entityManager); + return entityManager.createNativeQuery(sqlString, resultSetMapping); + } + + @Override + public StoredProcedureQuery createNamedStoredProcedureQuery(String name) { + tranTry(entityManager); + return entityManager.createNamedStoredProcedureQuery(name); + } + + @Override + public StoredProcedureQuery createStoredProcedureQuery(String procedureName) { + tranTry(entityManager); + return entityManager.createStoredProcedureQuery(procedureName); + } + + @Override + public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) { + tranTry(entityManager); + return entityManager.createStoredProcedureQuery(procedureName, resultClasses); + } + + @Override + public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) { + tranTry(entityManager); + return entityManager.createStoredProcedureQuery(procedureName, resultSetMappings); + } + + @Override + public void joinTransaction() { + entityManager.joinTransaction(); + } + + @Override + public boolean isJoinedToTransaction() { + return entityManager.isJoinedToTransaction(); + } + + @Override + public T unwrap(Class cls) { + return entityManager.unwrap(cls); + } + + @Override + public Object getDelegate() { + return entityManager.getDelegate(); + } + + @Override + public void close() { + entityManager.close(); + } + + @Override + public boolean isOpen() { + return entityManager.isOpen(); + } + + @Override + public EntityTransaction getTransaction() { + return entityManager.getTransaction(); + } + + @Override + public EntityManagerFactory getEntityManagerFactory() { + return entityManager.getEntityManagerFactory(); + } + + @Override + public CriteriaBuilder getCriteriaBuilder() { + return entityManager.getCriteriaBuilder(); + } + + @Override + public Metamodel getMetamodel() { + return entityManager.getMetamodel(); + } + + @Override + public EntityGraph createEntityGraph(Class rootType) { + return entityManager.createEntityGraph(rootType); + } + + @Override + public EntityGraph createEntityGraph(String graphName) { + return entityManager.createEntityGraph(graphName); + } + + @Override + public EntityGraph getEntityGraph(String graphName) { + return entityManager.getEntityGraph(graphName); + } + + @Override + public List> getEntityGraphs(Class entityClass) { + return entityManager.getEntityGraphs(entityClass); + } +} diff --git a/solon-plugin-data-sql/hibernate-solon-plugin/src/main/java/org/hibernate/solon/integration/PersistenceContextBeanInjector.java b/solon-plugin-data-sql/hibernate-solon-plugin/src/main/java/org/hibernate/solon/integration/PersistenceContextBeanInjector.java new file mode 100644 index 0000000000000000000000000000000000000000..fabf4073ab5cfc811e4f271d71aa720b485972a9 --- /dev/null +++ b/solon-plugin-data-sql/hibernate-solon-plugin/src/main/java/org/hibernate/solon/integration/PersistenceContextBeanInjector.java @@ -0,0 +1,63 @@ +package org.hibernate.solon.integration; + + +import org.hibernate.cfg.AvailableSettings; +import org.noear.snack.core.utils.StringUtil; +import org.noear.solon.Solon; +import org.noear.solon.core.BeanInjector; +import org.noear.solon.core.BeanWrap; +import org.noear.solon.core.Props; +import org.noear.solon.core.VarHolder; + +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; +import javax.persistence.PersistenceContext; +import javax.sql.DataSource; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author vanlin + * @className PersistenceContextBeanInjector + * @description + * @since 2024/6/28 10:07 + */ +public class PersistenceContextBeanInjector implements BeanInjector { + private final ConcurrentHashMap ENTITY_MANAGER_FACTORIES = new ConcurrentHashMap<>(); + + + @Override + public void doInject(VarHolder varH, PersistenceContext anno) { + varH.context().getWrapAsync(DataSource.class, (dsBw) -> { + if (dsBw.raw() instanceof DataSource) { + inject0(anno, varH, dsBw); + } + }); + + } + + private void inject0(PersistenceContext anno, VarHolder varH, BeanWrap dsBw) { + String unitName = anno.unitName(); + if (StringUtil.isEmpty(unitName)) { + unitName = "default"; + } + + final Props dsProps = Solon.cfg().getProp("jpa.unit." + unitName); + + final String datasource = dsProps.get("datasource"); + if (!Objects.equals(dsBw.name(), datasource)) { + // 数据源不匹配不进行注入操作 + return; + } + + final Props properties = dsProps.getProp("properties"); + + properties.put(AvailableSettings.DATASOURCE, dsBw.raw()); + + final EntityManagerFactory entityManagerFactory = ENTITY_MANAGER_FACTORIES.computeIfAbsent(unitName, + key -> Persistence.createEntityManagerFactory(key, properties)); + + varH.setValue(new EntityManagerProxy(entityManagerFactory.createEntityManager())); + } + +} diff --git a/solon-plugin-data-sql/hibernate-solon-plugin/src/main/java/org/hibernate/solon/integration/XPluginImpl.java b/solon-plugin-data-sql/hibernate-solon-plugin/src/main/java/org/hibernate/solon/integration/XPluginImpl.java index 79fb50c243febae699adf594a466448fb35b3280..fb3decad3d69ab05b1df13d8f7488a1edf657bcc 100644 --- a/solon-plugin-data-sql/hibernate-solon-plugin/src/main/java/org/hibernate/solon/integration/XPluginImpl.java +++ b/solon-plugin-data-sql/hibernate-solon-plugin/src/main/java/org/hibernate/solon/integration/XPluginImpl.java @@ -4,6 +4,7 @@ import org.hibernate.solon.annotation.Db; import org.noear.solon.core.AppContext; import org.noear.solon.core.Plugin; +import javax.persistence.PersistenceContext; import javax.persistence.spi.PersistenceProviderResolverHolder; import javax.sql.DataSource; @@ -24,5 +25,7 @@ public class XPluginImpl implements Plugin { context.subWrapsOfType(DataSource.class, HibernateAdapterManager::register); context.beanInjectorAdd(Db.class, new DbBeanInjector()); + // 标准 jpa PersistenceContext 注入支持 + context.beanInjectorAdd(PersistenceContext.class, new PersistenceContextBeanInjector()); } } diff --git a/solon-plugin-data-sql/hibernate-solon-plugin/src/test/resources/app.yml b/solon-plugin-data-sql/hibernate-solon-plugin/src/test/resources/app.yml index ebb6574c0a6b8acb6855f0e12a5d29af4429dd7b..7d0437bc8eee3a9f05400ec24ca439cddeb20ea9 100644 --- a/solon-plugin-data-sql/hibernate-solon-plugin/src/test/resources/app.yml +++ b/solon-plugin-data-sql/hibernate-solon-plugin/src/test/resources/app.yml @@ -42,6 +42,20 @@ jpa.test: connection: isolaction: 4 # 事务隔离级别 4 可重复度 +# 标准 jpa 持久化单元 配置 +jpa.unit.demo: + # 持久化单元绑定数据源 + datasource: db1 + # 持久化单元hibernate配置 + properties: + hibernate: + hbm2ddl: + auto: create + show_sql: false + format_sql: true + dialect: org.hibernate.dialect.MySQL8Dialect + connection: + isolaction: 4 # 事务隔离级别 4 可重复度 #默认hibernate配置 # 可选app.yml 或者hibernate.cf.xml