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提取指定列

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

  1. python csv文件和xlsx文件混杂时,提取指定列数据并合并

    这篇文章是R语言 如何合并csv文件(批量读取csv文件)的姊妹篇.提供更为强大的功能. 解决的问题是提取csv文件和xlsx文件混杂时,文件合并问题.具体来说,下面的代码是提取指定列文本,简单清洗后 ...

  2. 根据列名提取指定列 shell awk

    例子文件 colname.txt enzyme sample1 sample2 sample3 sample4 sample5 1 2 2 3 4 5 2 2 4 6 8 8 3 4 7 8 9 10 ...

  3. Arcgis提取指定面矢量内点或线数据

    Arcgis提取指定面矢量内点或线数据 本章导读:前人对面与面矢量的分割.联合等相关分析已经做了较多的分享工作.但对于指定面内点矢量数据和线矢量数据的提取经验分享相对而言较少.下面展示对流域内所有河段 ...

  4. python数据提取和合并_用Python提取和合并Excel数据

    我有一个Excel(.xlsx)文件,大约有40个工作表.每个工作表具有相同的结构,但包含不同的数据.我想从每张表格中提取信息,并将其合并到一张表格中,每张表格中的信息一张叠一张地叠在一起.我需要从每 ...

  5. delphi listview1添加指定列_对表格的列进行批量处理的函数详解

    Table.TransformColumns Table.TransformColumns(table as table, transformOperations as list, optional ...

  6. 批量提取文件创建时间_文件列表提取软件的使用

    前言 文件列表提取软件Directory Lister Pro在功能上和之前分享过的天乐批量提取文件名称软件类似,但又比天乐强大的多.同样,我们也可以利用Directory Lister Pro提取文 ...

  7. python 提取pdf表格_用Python提取pdf文件中的表格数据

    本文作者:杨慧琳 本文编辑:周聪聪 技术总编:张学人有问题,不要怕!访问 http://www.wuhanstring.com/uploads/5_aboutus/爬虫俱乐部-用户问题登记表.docx ...

  8. pb 修改数据窗口种指定字段位置_如何在PB数据窗口中修改数据---设置数据窗口的更新属性...

    如何在 PB 数据窗口中修改数据 --- 设置数据窗口的更新属性 数据窗口对象非常强大的原因之一就是能够很容易地修改数据库.当用户修 改了数据窗口中的数据,插入了新的数据行或者删除了数据行以后,只要调 ...

  9. python提取文件指定列_如何从csv文件中提取特定列并使用python绘图

    我有一个csv文件,其中包含以下几行数据:# Vertex X Y Z K_I K_II K_III J 0 2.100000e+00 2.000000e+00 -1.000000e-04 0.000 ...

最新文章

  1. “新视野”和“最远点”的约会
  2. jquery效果案例学习站
  3. 第三方登录接入-qq,weibo-java
  4. 零食嘴----美食领域的美丽说
  5. C语言经典算法100例-002-数轴的使用
  6. Kafka : WARN Error while fetching metadata with correlation id xx : {=UNKNOWN_TOPIC_OR_PARTITION}
  7. Numpy Binary operations
  8. IDEA中实现接口时注解@Override报错的解决方法
  9. 技术圈几个牛逼的公号推荐给大家
  10. 编译lua5.3.5报错:libreadline.so存在多处未定义的引用
  11. 推荐几款微信小程序常用组件库
  12. 索爱小蜜蜂扩音器怎么样啊~
  13. scp远程传输文件之权限被拒绝解决方案
  14. C语言中文网设计模式,C语言和设计模式(访问者模式)
  15. Gtarcade的Hunger Heroes游戏马拉松即将开始
  16. No7. 字符串匹配
  17. 使用jquery.form.js上传图片或文件
  18. 物价压力居高不下 低成本生活抠抠族各显神通
  19. 进制转换 PTA 7-6 IP地址转换
  20. Mysql出现问题:ERROR 1149 ( 42000 (ER_SYNTAX_ERROR)): You have an error in your SQL syntax; check th解决方案

热门文章

  1. A. And Then There Were K
  2. AT3968-[AGC025E] Walking on a Tree【构造】
  3. NOI.AC-积木【堆】
  4. CF613D-Kingdom and its Cities【虚树,LCA,树链剖分,贪心】
  5. P4945-最后的战役【dp,离散化】
  6. P3076,jzoj3187-的士【贪心】
  7. jzoj1295,P1607-轻轨(庙会班车)【贪心,线段树】
  8. 【jzoj】2018/2/2 NOIP普及组——D组模拟赛
  9. ssl1377-竞赛真理【dp之分组背包】
  10. 【模板】EK求最大流、dinic求最大流