在使用JPA侦听器进行数据库加密中,我讨论了使用JPA EntityListener进行透明加密。 从某种意义上说,这种方法是透明的,因为JPA实体(几乎)完全不知道正在加密,而JPA EntityListener本身也不知道细节。
有一个大问题。 EJB3可以将资源注入到EntityListener中。 春天不能。
(注意,我指的是JPA EntityListeners,而不是AOP方法。EntityListener必须在更严格的约束下工作。)
跳过一年,我有解决方案。 有一个很大的警告:仅当您知道您的JPA实现为Hibernate 4.0.0.Final或更高版本时,此方法才有效。 在您的设计中引入实现细节从来都不是一件有趣的事,但是幸运的是我们几乎可以隐藏所有细节。
休眠回调
Hibernate已经进行了多年的回调。 我不知道它们的使用范围-如果您不小心,它们就是会导致供应商锁定的东西。 供应商锁定就是我和其他许多人尝试保持纯JPA的原因。
遗憾的是,有时JPA还不够。
输入将近两年前的博客文章: 使用JPA的Spring管理的事件侦听器 。
切入Hibernate 4.0.0.Final后的代码,添加Hibernate侦听器很容易。 这些不是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适配器
上一个类是一个好的开始,但是注入弹簧的侦听器需要使用Hibernate实体侦听器注释,而不是标准的JPA javax.persistence注释。 我们可以做得更好吗?
快速的答案是肯定的-通过反思很简单。 见下文。
并非如此Swift的答案是肯定的,但是正确地进行反思可能很棘手。 您需要考虑超类和接口,例如,可以为Auditable接口而不是特定的类编写EntityListener。 排序可能变得很重要,尤其是在注释了一个类及其超类时。 我不会在下面的代码中假装解决这些问题。
“天哪,我们在想什么?” 答案是肯定的,但是可靠的解决方案还可以指定不检查超类和/或接口,指定或排除默认侦听器等。
在我们自己的代码中,很少有人会遇到这些情况。 图书馆作家必须担心它们,但是我们可以从最基本的假设开始,并仅在需要时添加它们。
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传递一个显式的bean列表。 我们可以在实体bean上使用注释吗? 与标准JPA @EntityListeners相比,将其称为@SpringEntityListeners,并将其传递给Bean类。
如果只有一种方法可以获取托管bean列表……。
有! JPA EntityManager提供了一种获取包含所有托管bean的元模型的方法。 我们可以扫描此列表以找到带注释的实体类。
编码:
@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中找到代码。 除了简单的记录程序实体侦听器和透明的密码加密实体侦听器之外,它还有一个基本的spring-data实现,可演示代码。
翻译自: https://www.javacodegeeks.com/2013/10/spring-injected-beans-in-jpa-entitylisteners.html