orm提取指定列_使用ORM提取数据很容易! 是吗?

orm提取指定列

介绍

几乎所有系统都以某种方式与外部数据存储一起运行。 在大多数情况下,它是一个关系数据库,并且通常将数据获取委托给某些ORM实现。 ORM涵盖了很多例程,并且带来了一些新的抽象作为回报。

Martin Fowler写了一篇有关ORM的有趣文章 ,其中的主要思想之一是:“ ORM帮助我们处理大多数企业应用程序中非常实际的问题。 …他们不是很好的工具,但是他们解决的问题也不是很可爱。 我认为他们应该得到更多的尊重和更多的理解。”

在CUBA框架中,由于我们在全球范围内有各种各样的项目,因此我们非常频繁地使用ORM,并且对它的局限性了解很多。 有很多事情可以讨论,但我们将集中讨论其中之一:懒惰与急切的数据获取。 我们将讨论数据获取的不同方法(主要是在JPA API和Spring中),我们如何在CUBA中处理数据以及我们为提高CUBA中的ORM层所做的RnD工作。 我们将研究一些基本要素,这些要素可能会帮助开发人员避免使用ORM带来糟糕的性能问题。

获取数据:懒惰还是渴望?

如果您的数据模型仅包含一个实体,那么使用ORM不会有任何问题。 让我们看一个例子。 我们有一个具有ID和名称的用户:

 public class User { @Id @GeneratedValue private int id; private String name; //Getters and Setters here  } 

要获取它,我们只需要很好地询问EntityManager即可:

 EntityManager em = entityManagerFactory.createEntityManager();  User user = em.find(User. class , id); 

当实体之间存在一对多关系时,事情就会变得有趣起来:

 public class User { @Id @GeneratedValue private int id; private String name; @OneToMany private List<Address> addresses; //Getters and Setters here  } 

如果要从数据库中获取用户记录,则会出现一个问题:“我们也应该获取地址吗?”。 正确的答案将是:“取决于”。 在某些用例中,我们可能需要其中一些地址-不需要。 通常,ORM提供两个用于获取数据的选项:惰性和渴望。 它们中的大多数默认情况下都设置了惰性获取模式。 而当我们编写以下代码时:

 EntityManager em = entityManagerFactory.createEntityManager();  User user = em.find(User. class , 1 );  em.close();  System.out.println(user.getAddresses().get( 0 )); 

我们得到了所谓的“LazyInitException” ,这使ORM新手非常困惑。 在这里,我们需要解释“附加”和“分离”对象的概念,并讲述数据库会话和事务。

好的,然后,应将实体实例附加到会话,以便我们能够获取详细信息属性。 在这种情况下,我们遇到了另一个问题–交易时间越来越长,因此陷入僵局的风险增加了。 而且,由于短查询的数量增加,将我们的代码拆分为一系列短事务可能会导致数据库“百万蚊子死亡”。

如前所述,您可能需要也可能不需要获取Addresses属性,因此仅在某些用例中需要“触摸”集合,从而添加更多条件。 嗯... 看起来越来越复杂。

好的,另一种提取类型会有所帮助吗?

 public class User { @Id @GeneratedValue private int id; private String name; @OneToMany (fetch = FetchType.EAGER) private List<Address> addresses; //Getters and Setters here  } 

好吧,不完全是。 我们将摆脱烦人的懒惰init异常,并且不应该检查实例是已附加还是已分离。 但是我们遇到了性能问题,因为同样,我们并不需要所有情况的地址,而是始终选择它们。 还有其他想法吗?

Spring JDBC

一些开发人员对ORM感到非常恼火,以至于他们使用Spring JDBC切换到“半自动”映射。 在这种情况下,我们将为唯一的用例创建唯一的查询,并返回包含仅对特定用例有效的属性的对象。

它给了我们极大的灵活性。 我们只能得到一个属性:

 String name = this .jdbcTemplate.queryForObject( "select name from t_user where id = ?" , new Object[]{1L}, String. class ); 

或整个对象:

 User user = this .jdbcTemplate.queryForObject( "select id, name from t_user where id = ?" , new Object[]{1L}, new RowMapper<User>() { public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setName(rs.getString( "name" )); user.setId(rs.getInt( "id" )); return user; } }); 

您也可以使用ResultSetExtractor来获取地址,但是它涉及编写一些额外的代码,并且您应该知道如何编写SQL联接以避免n + 1 select问题 。

好吧,它又变得越来越复杂。 您可以控制所有查询,也可以控制映射,但是您必须编写更多代码,学习SQL并知道如何执行数据库查询。 尽管我认为了解SQL基础知识对于几乎每个开发人员都是必不可少的技能,但其中一些人并不这么认为,因此我不会与他们争论。 如今,了解x86汇编器也不是每个人都必不可少的技能。 让我们考虑一下如何简化开发。

JPA实体图

让我们退后一步,尝试了解我们将要实现什么? 似乎我们需要做的就是准确告诉我们在不同用例中要获取哪些属性。 那我们做吧! JPA 2.1引入了新的API –实体图。 该API背后的想法很简单–您只需编写一些注释来描述应获取的内容。 让我们看一个例子:

 @Entity  @NamedEntityGraphs ({ @NamedEntityGraph (name = "user-only-entity-graph" ), @NamedEntityGraph (name = "user-addresses-entity-graph" , attributeNodes = { @NamedAttributeNode ( "addresses" )}) })  public class User { @Id @GeneratedValue private int id; private String name; @OneToMany (fetch = FetchType.LAZY) private Set<Address> addresses; //Getters and Setters here  } 

对于这个实体,我们描述了两个实体图– user-only-entity-graph不获取Addresses属性(标记为惰性),而第二个图则指示ORM选择地址。 如果我们将属性标记为渴望,则实体图设置将被忽略,并且将获取该属性。

因此,从JPA 2.1开始,您可以通过以下方式选择实体:

 EntityManager em = entityManagerFactory.createEntityManager();  EntityGraph graph = em.getEntityGraph( "user-addresses-entity-graph" );  Map<String, Object> properties = Map.of( "javax.persistence.fetchgraph" , graph);  User user = em.find(User. class , 1 , properties);  em.close(); 

这种方法极大地简化了开发人员的工作,无需“接触”惰性属性并创建长事务。 很棒的事情是,实体图可以应用于SQL生成级别,因此不会从数据库中将多余的数据提取到Java应用程序。 但是仍然有问题。 我们不能说获取了哪些属性,哪些没有。 为此有一个API,您可以使用PersistenceUnit类检查属性:

 PersistenceUtil pu = entityManagerFactory.getPersistenceUnitUtil();  System.out.println( "User.addresses loaded: " + pu.isLoaded(user, "addresses" "User.addresses loaded: " + pu.isLoaded(user, "addresses" )); 

但这很无聊。 我们可以简化一下,只是不显示未提取的属性吗?

Spring预测

Spring Framework提供了一个很棒的工具,称为Projections (它与Hibernate的Projections不同)。 如果我们只想获取实体的某些属性,则可以指定一个接口,Spring将从数据库中选择接口“实例”。 让我们看一个例子。 如果我们定义以下接口:

 interface NamesOnly { String getName();  } 

然后定义一个Spring JPA存储库以获取我们的User实体:

 interface UserRepository extends CrudRepository<User, Integer> { Collection<NamesOnly> findByName(String lastname);  } 

在这种情况下,调用findByName方法后,我们将无法访问未提取的属性! 同样的原则也适用于详细实体类。 因此,您可以通过这种方式获取主记录和明细记录。 此外,在大多数情况下,Spring会生成“适当的” SQL并仅获取投影中指定的属性,即,投影的工作方式类似于实体图描述。

这是一个非常强大的概念,您可以使用SpEL表达式,使用类而不是接口等。如果您有兴趣,可以在文档中查看更多信息。

投影的唯一问题是在幕后将它们实现为地图,因此是只读的。 因此,考虑到您可以为投影定义setter方法,则既不能使用CRUD存储库也不能使用EntityManager保存更改。 您可以将投影视为DTO,并且必须编写自己的DTO到实体的转换代码。

CUBA实施

从CUBA框架开发的开始,我们就尝试优化与数据库一起使用的代码。 在框架中,我们使用EclipseLink来实现数据访问层API。 关于EclipseLink的好处-它从一开始就支持部分实体加载,这就是为什么我们首先选择它而不是Hibernate的原因。 在此ORM中,您可以指定在JPA 2.1成为标准之前应确切加载哪些属性。 因此,我们将类似内部“实体图”的概念添加到我们的框架CUBA Views中 。 视图非常强大-您可以扩展它们,合并等等。CUBA视图创建背后的第二个原因-我们想要使用短事务,并专注于主要处理分离对象,否则,我们不能使富Web UI快速响应。

在CUBA视图中,描述存储在XML文件中,如下所示:

 <view class = "com.sample.User" extends = "_local" name= "user-minimal-view" > <property name= "name" /> <property name= "addresses" view= "address-street-only-view" /> </property>  </view> 

该视图指示CUBA DataManager提取具有其本地名称属性的User实体,并在查询级获取地址(重要!)的同时应用地址仅街道视图获取地址。 定义视图后,可以使用DataManager类将其应用于获取实体:

 List<User> users = dataManager.load(User. class ).view( "user-edit-view" ).list(); 

它的工作原理很像,并且由于不加载未使用的属性而节省了大量网络流量,但是像在JPA Entity Graph中一样,存在一个小问题:我们无法说出User实体的哪些属性已加载。 在CUBA中,我们“IllegalStateException: Cannot get unfetched attribute [...] from detached object”令人讨厌的“IllegalStateException: Cannot get unfetched attribute [...] from detached object” 。 像在JPA中一样,您可以检查是否未提取属性,但是为要提取的每个实体编写这些检查是一项无聊的工作,开发人员对此并不满意。

CUBA View Interfaces PoC

如果我们能充分利用两个世界的优势,该怎么办? 我们决定使用Spring的方法来实现所谓的实体接口,但是这些接口在应用程序启动期间会转换为CUBA视图,然后可以在DataManager中使用。 这个想法非常简单:您定义一个指定实体图的接口(或一组接口)。 它看起来像Spring Projections,并且像Entity Graph一样工作:

 interface UserMinimalView extends BaseEntityView<User, Integer> { String getName(); void setName(String val); List<AddressStreetOnly> getAddresses(); interface AddressStreetOnly extends BaseEntityView<Address, Integer> { String getStreet(); void setStreet(String street); }  } 

请注意,如果仅在一种情况下使用,则AddressStreetOnly接口可以嵌套。

在CUBA应用程序启动期间(实际上,大多数情况是Spring Context初始化),我们为CUBA视图创建了程序化表示并将其存储在Spring上下文中的内部存储库bean中。

之后,我们需要调整DataManager,以便它除了可以接受CUBA View字符串名称之外,还可以接受类名称,然后我们只需传递接口类即可:

 List<User> users = dataManager.loadWithView(UserMinimalView. class ).list(); 

我们为每个从数据库获取的实例生成代理来实现实体视图,就像冬眠一样。 并且,当您尝试获取属性的值时,代理会将调用转发给真实实体。

通过这种实现,我们试图用一块石头杀死两只鸟:

  • 接口中未说明的数据不会加载到Java应用程序代码中,从而节省了服务器资源
  • 开发人员仅使用获取的属性,因此不再使用“ UnfetchedAttribute”错误(在Hibernate中也称为LazyInitException )。

与Spring Projections相比,实体视图包装实体并实现CUBA的实体接口,因此可以将它们视为实体:您可以更新属性并将更改保存到数据库。

这里的“第三只鸟” –您可以定义一个仅包含吸气剂的“只读”接口,从而完全防止实体在API级别进行修改。

另外,我们可以对分离的实体执行一些操作,例如将该用户的名称转换为小写:

 @MetaProperty  default String getNameLowercase() { return getName().toLowerCase();  } 

在这种情况下,所有计算出的属性都可以从实体模型中移出,因此您不必将数据获取逻辑与用例特定的业务逻辑混合在一起。

另一个有趣的机会–您可以继承接口。 这使您可以准备具有不同属性集的多个视图,然后根据需要将它们混合。 例如,您可以有一个包含用户名和电子邮件的界面,以及另一个包含名和地址的界面。 而且,如果您需要第三个视图接口,其中应该包含名称,电子邮件和地址,则可以通过将二者结合在一起来实现–这要归功于Java中接口的多重继承。 请注意,您可以将此第三个接口传递给使用第一个或第二个接口的方法,OOP原理照常在这里工作。

我们还实现了视图之间的实体转换–每个实体视图都有reload()方法,该方法接受另一个视图类作为参数:

 UserFullView userFull = userMinimal.reload(UserFullView. class ); 

UserFullView可能包含其他属性,因此将从数据库重新加载该实体。 实体重新加载是一个懒惰的过程,仅当您尝试获取实体属性值时才执行。 我们之所以这样做是因为在CUBA中,我们有一个“网络”模块,可呈现丰富的UI,并可能包含自定义的REST控制器。 在此模块中,我们使用相同的实体,并且可以将其部署在单独的服务器上。 因此,每个实体重新加载都会通过核心模块(aka中间件)引起对数据库的附加请求。 因此,通过引入惰性实体重新加载,我们节省了一些网络流量和数据库查询。

PoC可以从GitHub下载-随时使用。

结论

ORM将在不久的将来在企业应用程序中大量使用。 我们只需要提供一些将数据库行转换为Java对象的工具即可。 当然,在复杂的高负载应用程序中,我们将继续看到独特的解决方案,但是ORM的生存时间将与RD​​BMSes一样长。

在CUBA框架中,我们试图简化ORM的使用,以使开发人员尽可能地轻松。 在下一版本中,我们将引入更多更改。 我不确定这些接口是视图接口还是其他接口,但是我可以肯定的是,使用CUBA在下一版本中使用ORM将得到简化。

翻译自: https://www.javacodegeeks.com/2019/09/fetching-data-with-orm-easy.html

orm提取指定列

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

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

相关文章

如何编译文件(gcc + nasm)

【0】README 编译文件(nasm gcc)&#xff0c;特别是编译目标文件的依赖文件&#xff1b;本文旨在回顾 gcc 和 nams 编译器的编译命令&#xff0c;以及如果有多个依赖文件该如何编译&#xff1b; 【1】编译文件 1.1&#xff09;编译 lib/syscall.asm(lib/syscall.asm) nasm …

Hibernate中的Session.load() 和 Session.get() 的区别

文章目录相同点区别懒加载的问题执行语句 session.load(UserInfoBean.class,2) 会先在一级缓存中找 id2 的 UserInfoBean 对象&#xff08;看红色线条&#xff09;&#xff0c;找不到去二级缓存找&#xff0c;还找不到就不找了&#xff0c;直接获取一个代理对象并返回该对象。当…

python 分布式队列_〖Python〗-- Celery分布式任务队列

【Celery分布式任务队列】一、Celery介绍和基本使用Celery 是一个 基于python开发的分布式异步消息任务队列&#xff0c;通过它可以轻松的实现任务的异步处理&#xff0c; 如果你的业务场景中需要用到异步任务&#xff0c;就可以考虑使用celery&#xff0c; 举几个实例场景中可…

最大子序列和问题的解(共4种,层层推进)

【0】README 0.1&#xff09; source code and text description are from data structure and alg analysis &#xff1b; 0.2&#xff09; there are 4 methods solving maximum sum of subsequence&#xff0c; but the fourth proves to be the best one , the 3rd deser…

java设计模式代理模式_Java中的代理设计模式

java设计模式代理模式代理对象或代理对象为另一个对象提供占位符&#xff0c;以控制对该对象的访问。 代理充当原始对象的轻量级版本或简化版本。 它支持与原始对象相同的操作&#xff0c;但可以将那些请求委托给原始对象以实现它们。 代理设计模式是一种结构模式&#xff0c;…

Struts2参数值自动注入到JavaBean对象的属性中和JavaBean对象的属性值展示在页面中

文章目录参数值自动注入示例代码将JavaBean的属性值显示在页面上示例代码参数值自动注入 注意事项&#xff1a; 1.前端界面的表单项的参数命名格式为&#xff1a;Action属性名.JavaBean的属性名 2.Action的属性必须要有setter/getter方法&#xff0c;注入时会调用setter方法 …

ibmmq 通道命令_IBM MQ常用命令

常用命令创建队列管理器crtmqm –q QMgrName-q是指创建缺省的队列管理器删除队列管理器dltmqm QmgrName启动队列管理器strmqm QmgrName如果是启动默认的队列管理器&#xff0c;可以不带其名字停止队列管理器endmqm QmgrName 受控停止endmqm –i QmgrName 立即停止endmqm –p Qm…

算法运行时间中的对数

【0】README 0.1&#xff09; source code and text description are from data structure and alg analysis &#xff1b; 【1】分析算法最混乱的方面大概集中在对数上面&#xff0c; 除分治算法外&#xff0c;可将对数最常出现的规律概括为下列一般法则&#xff1a; 1.1&a…

java项目:永和大王项目_Java项目:书评

java项目:永和大王项目本文是关于这本书的 Peter Verhas撰写的Java Projects Second Edition 我去年写的 这样一篇文章的目的通常是为了促进这本书的销售。 在这种情况下没有什么不同&#xff0c;但是由于这是我写的书&#xff0c;而且我是撰写评论的人&#xff0c;因此赞美这…

Struts2+Hibernate项目中的时间和日期问题

文章目录数据表中的 datetime 的数据如何通过 json 传送给前端仅展示其日期&#xff0c;而不展示时间日期控件日期时间数据展示问题日期时间数据存储问题场景一场景二场景三场景四数据表中的 datetime 的数据如何通过 json 传送给前端仅展示其日期&#xff0c;而不展示时间 数…

把一个人的特点写具体作文_把一个人的特点写具体作文

把一个人的特点写具体作文在日常学习、工作抑或是生活中&#xff0c;大家都跟作文打过交道吧&#xff0c;写作文可以锻炼我们的独处习惯&#xff0c;让自己的心静下来&#xff0c;思考自己未来的方向。那要怎么写好作文呢&#xff1f;下面是小编为大家整理的把一个人的特点写具…

spring boot简介_Spring Boot简介

spring boot简介在本教程中&#xff0c;我们将看一下Spring Boot&#xff0c;看看它与Spring框架有何不同。 我们还将讨论Spring Boot提供的各种功能。 什么是Spring Boot&#xff1f; 在开发企业级应用程序时&#xff0c;Spring是一个功能强大的框架。 它为我们提供了诸如依赖…

C语言的运算符的优先级与结合性+ASCII表

【0】README 0.1&#xff09; 内容来源于 C程序设计语言&#xff0c; 旨在整理出C语言的运算符的优先级与结合性&#xff0c; 如下图所示&#xff08;哥子 记了大半年都没有记住&#xff0c;也是醉了&#xff0c;每次都要去翻&#xff09;&#xff1b; 0.2&#xff09; 再补充…

退货退款的售后,如何返还金币/有偿优惠券的问题

假设买家购买了3个商品&#xff0c;商品的销售价是10元&#xff0c;商品总金额是30元 买家应付金额是 30 元&#xff0c;买家使用5个金币抵扣5元&#xff0c;买家实付金额是 25 元。 后来买家退货 2 件&#xff0c;怎么退款和退币呢&#xff1f; 要将金币分摊到每个商品上&a…

python orm框架sqlalchemy_python ORM框架:SqlAlchemy

ORM,对象关系映射&#xff0c;即Object Relational Mapping的简称&#xff0c;通过ORM框架将编程语言中的对象模型与数据库的关系模型建立映射关系&#xff0c;这样做的目的&#xff1a;简化sql语言操作数据库的繁琐过程(原生sql的编写及拼接等)&#xff0c;转而直接使用对象模…

javadoc提取工具_使JavaDoc保持最新状态的工具

javadoc提取工具在许多项目中&#xff0c;文档不是最新的。 更改代码后&#xff0c;很容易忘记更改文档。 原因是可以理解的。 在代码中进行更改&#xff0c;然后进行调试&#xff0c;然后希望在测试中进行更改&#xff08;或者&#xff0c;如果您使用的是更多TDD&#xff0c;则…

栈应用(中缀表达式转后缀表达式并计算后缀表达式的值)

【0】README 0.1&#xff09; 本文旨在总结 中缀表达式转后缀表达式并计算后缀表达式的值 的步骤&#xff0c;并给出源代码实现&#xff1b; 0.2&#xff09; 本文中涉及到的源代码均为原创&#xff0c;是对中缀转后缀和计算后缀的简单实现&#xff0c;&#xff08;旨在理清它…

用户/账户/账号的理解

文章目录用户账户账号关系用户 用户概念的理解&#xff1a; 用户就是使用者&#xff0c;可以是个人用户&#xff0c;也可以是机构用户。 账户 账户概念的理解&#xff1a; 账户&#xff0c;所谓“账”&#xff0c;就是系统根据“账”来存储和管理数据&#xff0c;类似记账…

azure 部署java_jClarity:在Azure上升级到Java

azure 部署java在互联世界公共基础设施的新时代&#xff0c;最大和最重要的两个方面是Java和OpenJDK的诞生和兴起。 因此&#xff0c;许多公司将时间和资源投入到构建最先进的技术上&#xff0c;以确保整个行业在未来几年内在AdoptOpenJDK上拥有丰富的质量&#xff0c;而且免费…

黑苹果sd卡认不出来_天生一对:新入苹果M1笔记本,DOCKCASE拓展坞弥补缺憾

2010年刚上大学那会&#xff0c;入手了人生第一台笔记本电脑&#xff0c;两边密密麻麻的各种接口&#xff0c;也没感觉到接口多少的价值&#xff1b;2016年年中入手了人生第一台苹果笔记本&#xff0c;第一次觉得电脑接口不够用&#xff1b;如今四年过去了电脑也到了更新换代的…