JPA和领域驱动的设计不能很好地结合在一起。事实上,JPA“实体”是Fowler警告所反对的贫血数据模型的代表。我已经讨论过因此,我不会在这里详细介绍贫血数据模型是否不好(请记住:我不认为它是坏的)。
但是,对于需要将依赖项注入实体的时间,标准不会帮助您。唯一的选择是@Configable,这需要AspectJ编织,这对我来说是“黑魔法”。那么,为什么不能将依赖项注入实体呢?因为它们是由您创建的,而不是由依赖注入框架创建的。
好吧,有个奇怪的主意。如果JPA 3有EntityManager.createEntity(Foo.class)让你的实体一直被管理?然后,您将能够注入与其他实体不同的依赖项(JPA已经通过关联处理)。它将如何工作?
- JPA将需要一个DependencyManagerCdi当然是默认的依赖关系管理器,但是任何框架(Spring、guice等)都可以插入它们的实现。
- 无论何时创建(.)您的实体将有它的依赖项集--就像该实体是由您的DI框架创建的一样。
- 这意味着实体的“瞬态”状态将不存在(如果选择create()方法,即)
但…分离的实体,或者需要创建不应该访问实体管理器(例如控制器中)的实体的情况,是最重要的事情。我不能给你一个满意的建议。您要么需要注入EntityCreator,后者将使用实体管理器,要么使用另一个机制(如果您可能从JPA切换到其他机制)。但是这可能是一个“最佳实践”,而不是一个标准的接口,因为如果它是一个JPA接口,它仍然是Web层对JPA的直接依赖。另一种选择是通过所有层传播创建,但这是样板。第三种选择是,当分离的实体“进入”服务层时(通过AOP)自动附加。这个想法的一个缺点是人们会被诱惑注入EntityManager本身,这将违反层边界。另一个缺点是人们可能会(正确地)说:“什么,我不能再用新的接线员了?”
所以我只是提出一个想法。如果CDI和JPA的设计师没有想到类似的东西,我会很惊讶,所以我想听听一些(更多)的反对意见。
MySQL中排序规则的常用选择是UTF 8_General_ci(或类似于aver_ci)。Ci的意思是“不区分大小写”(也有_cs排序规则)。这一切都很好,但是如果您正在存储并比较base 64编码的数据,您可能有一个隐藏的问题。base 64编码的数据区分大小写,但与_ci排序规则的比较不区分大小写。因此,您正在存储一些base 64,然后尝试查询它:
用户1 base 64:A543rFgTregW
User2base 64:A543rFGtregw
当你SELECt * FROM table WHERe base64column="..."您可能会得到错误的用户。
我同意这是很少见的(因为您应该很少查询二进制数据,不管表示形式如何),但如果发生这种情况,可能会浪费大量时间(幸运的是,我没有遇到这种情况)。
https://www.douban.com/doulist/145814547/
解决办法是使用VARBINARY.
然后我开始调试,并意识到应用程序实际上使用了会话-每个应用程序的反模式。不仅如此,每个DAO都有自己的实体管理器(和底层会话)实例。
这里有一个重要的注意事项--我说的是“实体管理器和底层会话”,因为Hibernate只是用标准EntityManager接口的实现来包装它的会话。因此,无论我们谈论实体管理器还是会话,都会有一些不同。
https://www.douban.com/doulist/145585807/
会发生什么?PerssitenceContextType.EXTENDED这意味着你,而不是春天,负责管理你的会议。Spring所做的就是在启动时创建它,在关闭时关闭它。另一个选项(这是默认的)-PerssitenceContextType.TRANSACTION)让Spring的事务管理器为每个请求创建实体管理器(和会话),启动事务,并在完成后-提交事务并关闭会话。
https://movie.douban.com/doulist/145585807/
这称为会话和事务管理,它是Spring&JPA项目中最重要的工作之一。几乎从一开始就应该把它做好,所以下次你做这样的项目时,多花几天时间来做好这件事。
但上述情况有何问题呢?下面是扩展管理器的一些效果:
- 每个DAO都有一个不同的EntityManager实例,因此您不能执行涉及两个DAO的任何有意义的工作。如果您在一个EntityManager中插入一个记录,并尝试在另一个中使用它,它将无法工作。第一个没有被刷新,第二个没有第一级缓存。
- 如果会话未被关闭,它会积累实体(将它们存储在内存中,这样就不必多次从DB获取它们)。这是一个纯粹的内存泄漏。
- 它不是线程安全的。会话和EntityManager对象不是线程安全的.由于您最有可能将它们注入到单例DAO对象中,因此由于并发访问,您将开始获得奇怪的结果。
这是怎么发生的,为什么这么久没人注意到?我有个理论。1.人们开始直接从Web层2开始使用DAO层,某些东西对某些人无效,所以他改变了持久性上下文的类型,这使他的代码工作。3.由于该项目插入量不多,人们主要是阅读数据,并没有遇到各种问题。4.还没有对同一实例中的多个人进行广泛的测试,因此没有发现并发问题。
https://movie.douban.com/doulist/145814547/
还有一件事-PersistenceContextType.EXTENDED在有限的情况下很有用。所谓长时间的会话或每次会话。当您有向导时,您可以使用同一个会话拥有多个请求,这将节省一些分离和合并。但是,如果您使用它,请确保您没有为整个应用程序这样做,并且您绝对知道自己在做什么。在对话结束时关闭会话。另一个场景是EJB有状态bean中的使用。(一般来说,扩展持久性上下文在JavaEE环境中更有意义)
因此,总结一下:
- 花费大量时间来正确配置会话和事务管理,并努力使其正确。
- 几乎从不使用PersistenceContextType.EXTENDED春季(至少在JavaEE容器之外)
- 不要直接从Web层使用DAO层。对于最常用的操作,带有包装器的基本服务类不会太冗长,但会给您带来无数麻烦。
- 代码评审应该是彻底的,或者对核心类和配置的提交权限应该仅限于一些知道自己在做什么的人。



