JPA EntityListeners中的Spring注入的Bean

在使用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实现,可演示代码。

参考: Invariant Properties博客的JPA 合作伙伴 Bear Giles 在JPA EntityListeners中注入了弹簧注入的Bean 。

翻译自: https://www.javacodegeeks.com/2013/10/spring-injected-beans-in-jpa-entitylisteners.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366890.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php sql 去除重复数据,MSSQL_快速删除重复记录,SQL Server如何实现?,如果一张表的数据达到上百万 - phpStudy...

如果一张表的数据达到上百万条&#xff0c;用游标的方法来删除简直是个噩梦&#xff0c;因为它会执行相当长的一段时间……开发人员的噩梦——删除重复记录想必每一位开发人员都有过类似的经历&#xff0c;在对数据库进行查询或统计的时候不时地会碰到由于表中存在重复的记录而…

Python运行的方式

Python的运行方式多种多样&#xff0c;下面列举几种: 交互式 在命令行中输入python&#xff0c;然后在>>>提示符后面输入Python语句&#xff0c;这里需要注意: 1 语句前面不能有空格&#xff0c;否则会报错 2 对于符合语句&#xff0c;前面会有...提示符&#xff0c;结…

使用Apache Zookeeper进行协调和服务发现

面向服务的设计已被证明是针对各种不同的分布式系统的成功解决方案。 如果使用得当&#xff0c;它会带来很多好处。 但是随着服务数量的增加&#xff0c;了解部署什么以及部署在何处变得更加困难。 而且&#xff0c;由于我们正在构建可靠且高度可用的系统&#xff0c;因此还需要…

微信小程序无埋点数据采集方案

作者&#xff1a;lxj&#xff0c;点餐终端团队成员前言 相信业务团队对这样的场景不会太陌生&#xff1a;打点需求&#xff1a; 每新上一个功能&#xff0c;数据产品便会同步加上打点需求&#xff0c;当数据打点上线后一段时间&#xff0c;数据产品/业务产品便会针对数据的转化…

php 状态码302,HTTP状态码302、303和307的故事

今日读书&#xff0c;无法理解HTTP302、303、307状态码的来龙去脉&#xff0c;决定对其做深究并总结于本文。《HTTP权威指南》第3章在讲解30X状态码时&#xff0c;完全没有讲清楚为什么要有302、303、307&#xff0c;以及他们的关系&#xff0c;一句“问题出在HTTP/1/1”让我一…

Codeforces 1070A Find a Number(BFS) 2018-2019 ICPC, NEERC, Southern Subregional Contest Problem A

Description You are given two positive integers ddd and sss. Find minimal positive integer nnn which is divisible by ddd and has sum of digits equal to sss.Input The first line contains two positive integers ddd and sss(1≤d≤500,1≤s≤5000)(1≤d≤500,1≤s…

luogu3807 【模板】 卢卡斯定理

题目大意 对于一个很大的$n,m,p$如何求$C_{nm}^m\mod p$&#xff1f; Lucas定理 若$n_i,m_i$分别是$n,m$在$p$进制下第$i$位的数字&#xff0c;则有 $$C_n^m\mod p\prod_{i0}^{\log_p m}C_{n_i}^{m_i}\mod p$$ 求法 按照定理式一个一个求组合数即可。 组合数并不用批量求。故预…

防止System.exit调用

在开发运行其他开发人员编写的代码的容器时&#xff0c;请谨慎防范System.exit调用。 如果开发人员无意间调用了System.exit并将其代码部署为由您的容器运行&#xff0c;则它将完全降低容器进程。 可以使用SecurityManager中的checkExit函数调用来控制。 根据SecurityManager …

php异步请求$.post,如何用PHP实现异步请求、忽略返回值

本篇文章的主要内容是用PHP实现异步请求、忽略返回值&#xff0c;具有一定的参考价值&#xff0c;有需要的朋友可以看看&#xff0c;希望能对你有帮助。项目需要&#xff0c;场景如下&#xff1a;某个条件下需要调用接口发送多个请求执行脚本&#xff0c;但是由于每个请求下的脚…

JavaScript函数式编程学习

本文是我在函数式编程学习过程中&#xff0c;总结的笔记&#xff0c;也分享给想学函数式编程的同学。 函数式编程可能对于初学者来说&#xff0c;概念难以理解&#xff0c;入门比较难。所以本文从两部分去学习。 在你身边你左右 --函数式编程别烦恼 第一部分&#xff0c;你身…

读书印记 - 《沟通的艺术:一本手把手教你社交沟通的书》

一个教训是没上过榜也没什么好推荐的书有挺大概率不是本好书。我现在不太能耐心的去读这种指导手册&#xff0c;一是谈话的技巧需要在实战中才能有效提升&#xff0c;二是这方面对我来说不是关注的重点。所以几乎是刷刷的翻完了整本书。当然也不能说这本书就是烂书&#xff0c;…

Linux NTP

ntpd服务的设置: ntpd服务的相关设置文件如下&#xff1a; /etc/ntp.conf&#xff1a;这个是NTP daemon的主要设文件&#xff0c;也是 NTP 唯一的设定文件。/usr /share/zoneinfo/:在这个目录下的文件其实是规定了各主要时区的时间设定文件&#xff0c;例如北京地区的时区设定文…

Java Web项目的保存和刷新

如何配置计算机以保存并刷新Java Web项目&#xff1f; 当您向开发人员提出此问题时&#xff0c;答案以“哦&#xff0c;好吧……”开头&#xff0c;并以某种可行的方式继续描述。 每个人都有自己的方式。 首先–为什么需要这个&#xff1f; 因为执行复制和重新启动服务器是生产…

mysql alter怎么用,mysql alter话语用法

mysql alter语句用法//主键ALTER TABLE tablename add new_field_id int(5) UNSIGNED DEFAULT 0 NOT NULL anto_increment,ADD PRIMARY KEY (new_field_id);//增加一个新列mysql>ALTER TABLE tablename ADD fieldname fieldtype如&#xff1a;ALTER TABLE t2 ADD d TIMESTAM…

《React 学习之道》The Road to learn React (简体中文版)

通往 React 实战大师之旅&#xff1a;掌握 React 最简单&#xff0c;且最实用的教程。 前言 《React 学习之道》这本书使用路线图中的精华部分用于传授 React&#xff0c;并将其融入一个独具吸引力的真实世界 App的具体代码实现。 如何掌握 React 存在特别多的路线图。本书提…

Linux下设置和查看环境变量

Linux的变量种类 按变量的生存周期来划分&#xff0c;Linux变量可分为两类&#xff1a; 1 永久的&#xff1a;需要修改配置文件&#xff0c;变量永久生效。 2 临时的&#xff1a;使用export命令声明即可&#xff0c;变量在关闭shell时失效。 设置变量的三种方法 1 在/etc/profi…

vue.js(三)

这里该记到vue的组件了&#xff0c;组件基础篇 1.vue组件的基本书写方式 Vue.component(button-counter, {data: function () {return {count: 0}},template: <button v-on:click"count">You clicked me {{ count }} times.</button> }) 上面定义了一个名…

RIP GlassFish –感谢所有的鱼。

我们都听说过它的到来。 昨天&#xff0c;JavaEE和GlassFish的官方路线图更新已发布 。 从标题开始&#xff0c;整个帖子基本上都是关于一件事的&#xff1a;今天我们知道的GlassFish Server已从完整的产品转为玩具产品。 从Sun到Oracle的漫长道路 从一开始&#xff0c;GlassF…

Nuxt中如何使用Vuex-Store异步获取数据

Nuxt是一个基于Vue.js的通用型框架&#xff0c;它集成了使用Vue开发的绝大数组件/框架。 长话短说如何在Vuex-store中获取异步数据呢&#xff1f; 在Nuxt中由于集合了Vuex还有其他的一些配置&#xff0c;大大的方便了我们使用Vuex&#xff1b;在Nuxt官方文档中写到&#xff1a;…

Struts2面试问答

Struts2是用Java开发Web应用程序的著名框架之一。 最近&#xff0c;我写了很多Struts2教程 &#xff0c;在这篇文章中&#xff0c;我列出了一些重要的Struts2面试问题以及答案&#xff0c;以帮助您进行面试。 什么是Struts2&#xff1f; Struts1和Struts2之间有什么区别&…