javascript
JPA EntityListeners中的Spring注入的Bean
在使用JPA偵聽(tīng)器進(jìn)行數(shù)據(jù)庫(kù)加密中,我討論了使用JPA EntityListener進(jìn)行透明加密。 從某種意義上說(shuō),這種方法是透明的,因?yàn)镴PA實(shí)體(幾乎)完全不知道正在加密,而JPA EntityListener本身也不知道細(xì)節(jié)。
有一個(gè)大問(wèn)題。 EJB3可以將資源注入到EntityListener中。 春天不能。
(注意,我指的是JPA EntityListeners,而不是AOP方法。EntityListener必須在更嚴(yán)格的約束下工作。)
跳過(guò)一年,我有解決方案。 有一個(gè)很大的警告:僅當(dāng)您知道您的JPA實(shí)現(xiàn)為Hibernate 4.0.0.Final或更高版本時(shí),此方法才有效。 在您的設(shè)計(jì)中引入實(shí)現(xiàn)細(xì)節(jié)從來(lái)都不是一件有趣的事,但是幸運(yùn)的是我們幾乎可以隱藏所有細(xì)節(jié)。
休眠回調(diào)
Hibernate已經(jīng)進(jìn)行了多年的回調(diào)。 我不知道它們的使用范圍-如果您不小心,它們就是會(huì)導(dǎo)致供應(yīng)商鎖定的東西。 供應(yīng)商鎖定就是我和其他許多人嘗試保持純JPA的原因。
遺憾的是,有時(shí)JPA還不夠。
輸入將近兩年前的博客文章: 使用JPA的Spring管理的事件偵聽(tīng)器 。
切入Hibernate 4.0.0.Final后的代碼,添加Hibernate偵聽(tīng)器很容易。 這些不是JPA EntityListener,但讓我們一次僅一步。
/*** Configure Spring-aware entity listeners. This implementation is* hibernate-specific.* * See: http://deepintojee.wordpress.com/2012/02* /05/spring-managed-event-listeners-with-jpa/* * Another approach follows, but it doesn't support Spring injection.* http://stackoverflow.com/questions/8616146/eventlisteners-using-hibernate* -4-0-with-spring-3-1-0-release* * @author louis.gueye@gmail.com (see above)* @author Bear Giles <bgiles@coyotesong.com>*/ @Component public class HibernateListenersConfigurer {private static final Logger log = LoggerFactory.getLogger(HibernateListenersConfigurer.class);@Resourceprivate EntityManagerFactory emf;@Resourceprivate HibernateListenersAdapter listener;@PostConstructpublic void registerListeners() {HibernateEntityManagerFactory hemf = (HibernateEntityManagerFactory) emf;SessionFactory sf = hemf.getSessionFactory();EventListenerRegistry registry = ((SessionFactoryImpl) sf).getServiceRegistry().getService(EventListenerRegistry.class);registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(listener);registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener(listener);registry.getEventListenerGroup(EventType.PRE_UPDATE).appendListener(listener);registry.getEventListenerGroup(EventType.POST_COMMIT_UPDATE).appendListener(listener);registry.getEventListenerGroup(EventType.PRE_DELETE).appendListener(listener);registry.getEventListenerGroup(EventType.POST_COMMIT_DELETE).appendListener(listener);registry.getEventListenerGroup(EventType.POST_LOAD).appendListener(listener);} }Hibernate / JPA適配器
上一個(gè)類(lèi)是一個(gè)好的開(kāi)始,但是注入彈簧的偵聽(tīng)器需要使用Hibernate實(shí)體偵聽(tīng)器注釋,而不是標(biāo)準(zhǔn)的JPA javax.persistence注釋。 我們可以做得更好嗎?
快速的答案是肯定的-通過(guò)反思很簡(jiǎn)單。 見(jiàn)下文。
并非如此Swift的答案是肯定的,但是正確地進(jìn)行反思可能很棘手。 您需要考慮超類(lèi)和接口,例如,可以為Auditable接口而不是特定的類(lèi)編寫(xiě)EntityListener。 排序可能變得很重要,尤其是在注釋了一個(gè)類(lèi)及其超類(lèi)時(shí)。 我不會(huì)在下面的代碼中假裝解決這些問(wèn)題。
“天哪,我們?cè)谙胧裁?#xff1f;” 答案是肯定的,但是可靠的解決方案還可以指定不檢查超類(lèi)和/或接口,指定或排除默認(rèn)偵聽(tīng)器等。
在我們自己的代碼中,很少有人會(huì)遇到這些情況。 圖書(shū)館作家必須擔(dān)心它們,但是我們可以從最基本的假設(shè)開(kāi)始,并僅在需要時(shí)添加它們。
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.LinkedHashMap; import java.util.List; import java.util.Map;import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.persistence.Entity; import javax.persistence.EntityManagerFactory; import javax.persistence.PostLoad; import javax.persistence.PostPersist; import javax.persistence.PostRemove; import javax.persistence.PostUpdate; import javax.persistence.PrePersist; import javax.persistence.PreRemove; import javax.persistence.PreUpdate;import org.apache.log4j.Logger; import org.hibernate.SessionFactory; import org.hibernate.ejb.HibernateEntityManagerFactory; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.PostDeleteEvent; import org.hibernate.event.spi.PostDeleteEventListener; import org.hibernate.event.spi.PostInsertEvent; import org.hibernate.event.spi.PostInsertEventListener; import org.hibernate.event.spi.PostLoadEvent; import org.hibernate.event.spi.PostLoadEventListener; import org.hibernate.event.spi.PostUpdateEvent; import org.hibernate.event.spi.PostUpdateEventListener; import org.hibernate.event.spi.PreDeleteEvent; import org.hibernate.event.spi.PreDeleteEventListener; import org.hibernate.event.spi.PreInsertEvent; import org.hibernate.event.spi.PreInsertEventListener; import org.hibernate.event.spi.PreUpdateEvent; import org.hibernate.event.spi.PreUpdateEventListener; import org.hibernate.internal.SessionFactoryImpl;/*** Adapter that allows a Hibernate event listener to call a standard JPA* EntityListener.* * For simplicity only a single bean of each class is supported. It is not* difficult to support multiple beans, just messy.* * Each listener can have multiple methods with the same annotation.* * @author Bear Giles <bgiles@coyotesong.com>*/ public class HibernateListenersAdapter implements PostInsertEventListener, PreInsertEventListener,PreUpdateEventListener, PostUpdateEventListener, PreDeleteEventListener, PostDeleteEventListener,PostLoadEventListener {private static final long serialVersionUID = 1L;private static final Logger log = Logger.getLogger(HibernateListenersAdapter.class);@Resourceprivate List<Object> listeners;@Resourceprivate EntityManagerFactory emf;private Map<Class, Map<Method, Object>> preInsert = new LinkedHashMap<Class, Map<Method, Object>>();private Map<Class, Map<Method, Object>> postInsert = new LinkedHashMap<Class, Map<Method, Object>>();private Map<Class, Map<Method, Object>> preUpdate = new LinkedHashMap<Class, Map<Method, Object>>();private Map<Class, Map<Method, Object>> postUpdate = new LinkedHashMap<Class, Map<Method, Object>>();private Map<Class, Map<Method, Object>> preRemove = new LinkedHashMap<Class, Map<Method, Object>>();private Map<Class, Map<Method, Object>> postRemove = new LinkedHashMap<Class, Map<Method, Object>>();private Map<Class, Map<Method, Object>> postLoad = new LinkedHashMap<Class, Map<Method, Object>>();private EventListenerRegistry registry;@PostConstructpublic void findMethods() {for (Object listener : listeners) {findMethodsForListener(listener);}HibernateEntityManagerFactory hemf = (HibernateEntityManagerFactory) emf;SessionFactory sf = hemf.getSessionFactory();registry = ((SessionFactoryImpl) sf).getServiceRegistry().getService(EventListenerRegistry.class);}public void findMethodsForListener(Object listener) {Class<?> c = listener.getClass();for (Method m : c.getMethods()) {if (Void.TYPE.equals(m.getReturnType())) {Class<?>[] types = m.getParameterTypes();if (types.length == 1) {// check for all annotations now...if (m.getAnnotation(PrePersist.class) != null) {if (!preInsert.containsKey(types[0])) {preInsert.put(types[0], new LinkedHashMap<Method, Object>());}preInsert.get(types[0]).put(m, listener);}if (m.getAnnotation(PostPersist.class) != null) {if (!postInsert.containsKey(types[0])) {postInsert.put(types[0], new LinkedHashMap<Method, Object>());}postInsert.get(types[0]).put(m, listener);}if (m.getAnnotation(PreUpdate.class) != null) {if (!preUpdate.containsKey(types[0])) {preUpdate.put(types[0], new LinkedHashMap<Method, Object>());}preUpdate.get(types[0]).put(m, listener);}if (m.getAnnotation(PostUpdate.class) != null) {if (!postUpdate.containsKey(types[0])) {postUpdate.put(types[0], new LinkedHashMap<Method, Object>());}postUpdate.get(types[0]).put(m, listener);}if (m.getAnnotation(PreRemove.class) != null) {if (!preRemove.containsKey(types[0])) {preRemove.put(types[0], new LinkedHashMap<Method, Object>());}preRemove.get(types[0]).put(m, listener);}if (m.getAnnotation(PostRemove.class) != null) {if (!postRemove.containsKey(types[0])) {postRemove.put(types[0], new LinkedHashMap<Method, Object>());}postRemove.get(types[0]).put(m, listener);}if (m.getAnnotation(PostLoad.class) != null) {if (!postLoad.containsKey(types[0])) {postLoad.put(types[0], new LinkedHashMap<Method, Object>());}postLoad.get(types[0]).put(m, listener);}}}}}/*** Execute the listeners. We need to check the entity's class, parent* classes, and interfaces.* * @param map* @param entity*/private void execute(Map<Class, Map<Method, Object>> map, Object entity) {if (entity.getClass().isAnnotationPresent(Entity.class)) {// check for hits on this class or its superclasses.for (Class c = entity.getClass(); c != null && c != Object.class; c = c.getSuperclass()) {if (map.containsKey(c)) {for (Map.Entry<Method, Object> entry : map.get(c).entrySet()) {try {entry.getKey().invoke(entry.getValue(), entity);} catch (InvocationTargetException e) {// log it} catch (IllegalAccessException e) {// log it}}}}// check for hits on interfaces.for (Class c : entity.getClass().getInterfaces()) {if (map.containsKey(c)) {for (Map.Entry<Method, Object> entry : map.get(c).entrySet()) {try {entry.getKey().invoke(entry.getValue(), entity);} catch (InvocationTargetException e) {// log it} catch (IllegalAccessException e) {// log it}}}}}}/*** @see org.hibernate.event.spi.PostDeleteEventListener#onPostDelete(org.hibernate* .event.spi.PostDeleteEvent)*/@Overridepublic void onPostDelete(PostDeleteEvent event) {execute(postRemove, event.getEntity());}/*** @see org.hibernate.event.spi.PreDeleteEventListener#onPreDelete(org.hibernate* .event.spi.PreDeleteEvent)*/@Overridepublic boolean onPreDelete(PreDeleteEvent event) {execute(preRemove, event.getEntity());return false;}/*** @see org.hibernate.event.spi.PreInsertEventListener#onPreInsert(org.hibernate* .event.spi.PreInsertEvent)*/@Overridepublic boolean onPreInsert(PreInsertEvent event) {execute(preInsert, event.getEntity());return false;}/*** @see org.hibernate.event.spi.PostInsertEventListener#onPostInsert(org.hibernate* .event.spi.PostInsertEvent)*/@Overridepublic void onPostInsert(PostInsertEvent event) {execute(postInsert, event.getEntity());}/*** @see org.hibernate.event.spi.PreUpdateEventListener#onPreUpdate(org.hibernate* .event.spi.PreUpdateEvent)*/@Overridepublic boolean onPreUpdate(PreUpdateEvent event) {execute(preUpdate, event.getEntity());return false;}/*** @see org.hibernate.event.spi.PostUpdateEventListener#onPostUpdate(org.hibernate* .event.spi.PostUpdateEvent)*/@Overridepublic void onPostUpdate(PostUpdateEvent event) {execute(postUpdate, event.getEntity());}/*** @see org.hibernate.event.spi.PostLoadEventListener#onPostLoad(org.hibernate* .event.spi.PostLoadEvent)*/@Overridepublic void onPostLoad(PostLoadEvent event) {execute(postLoad, event.getEntity());} }@SpringEntityListeners
這種方法要求向HibernateListenersConfigurer bean傳遞一個(gè)顯式的bean列表。 我們可以在實(shí)體bean上使用注釋嗎? 與標(biāo)準(zhǔn)JPA @EntityListeners相比,將其稱(chēng)為@SpringEntityListeners,并將其傳遞給Bean類(lèi)。
如果只有一種方法可以獲取托管bean列表……。
有! JPA EntityManager提供了一種獲取包含所有托管bean的元模型的方法。 我們可以掃描此列表以找到帶注釋的實(shí)體類(lèi)。
編碼:
@Component public class SpringEntityListenersConfigurer implements ApplicationContextAware {private static final Logger log = LoggerFactory.getLogger(SpringEntityListenersConfigurer.class);private ApplicationContext context;@Resourceprivate EntityManagerFactory entityManagerFactory;@Overridepublic void setApplicationContext(ApplicationContext context) {this.context = context;}@PostConstructpublic void registerListeners() {// get registry so we can add listeners.HibernateEntityManagerFactory hemf = (HibernateEntityManagerFactory) entityManagerFactory;SessionFactory sf = hemf.getSessionFactory();EventListenerRegistry registry = ((SessionFactoryImpl) sf).getServiceRegistry().getService(EventListenerRegistry.class);final Set<Object> listeners = new HashSet<Object>();EntityManager entityManager = null;try {entityManager = hemf.createEntityManager();// for every entity known to the system...for (EntityType<?> entity : entityManager.getMetamodel().getEntities()) {// ... register event listeners for it.if (entity.getJavaType().isAnnotationPresent(SpringEntityListeners.class)) {SpringEntityListeners annotation = (SpringEntityListeners) entity.getJavaType().getAnnotation(SpringEntityListeners.class);for (Class<?> beanClass : annotation.value()) {Map<String, ?> map = context.getBeansOfType(beanClass);listeners.addAll(map.values());}}}} finally {if (entityManager != null) {entityManager.close();}}// register adapter and listeners.HibernateEntityListenersAdapter adapter = new HibernateEntityListenersAdapter(new ArrayList<Object>(listeners),entityManagerFactory);registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(adapter);registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener(adapter);registry.getEventListenerGroup(EventType.PRE_UPDATE).appendListener(adapter);registry.getEventListenerGroup(EventType.POST_COMMIT_UPDATE).appendListener(adapter);registry.getEventListenerGroup(EventType.PRE_DELETE).appendListener(adapter);registry.getEventListenerGroup(EventType.POST_COMMIT_DELETE).appendListener(adapter);registry.getEventListenerGroup(EventType.POST_LOAD).appendListener(adapter);} } 可以在http://code.google.com/p/invariant-properties-blog/source/browse/spring-entity-listener中找到代碼。 除了簡(jiǎn)單的記錄程序?qū)嶓w偵聽(tīng)器和透明的密碼加密實(shí)體偵聽(tīng)器之外,它還有一個(gè)基本的spring-data實(shí)現(xiàn),可演示代碼。
翻譯自: https://www.javacodegeeks.com/2013/10/spring-injected-beans-in-jpa-entitylisteners.html
總結(jié)
以上是生活随笔為你收集整理的JPA EntityListeners中的Spring注入的Bean的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【深度学习】Subword Tokeni
- 下一篇: Spring Data Solr入门