【问题标题】:JPA: dynamic queries with ResultSet-like access / SQL-JPA?JPA:具有类 ResultSet 访问/SQL-JPA 的动态查询?
【发布时间】:2014-08-22 20:43:04
【问题描述】:

初步:代码存在于使用 JPA 并且对它感到满意的 Java-Webapp 中,因此完全放弃 JPA 不是解决方案。

但我的部分是关于动态数据查询。例如,管理员可以编写 sql 查询,用户可以触发这些查询来生成 csv-exports。

使用 JDBC,我可以获得很多关于 ResultSet 的元数据,并且我可以在我知道的地方获取字符串和整数。

JPA 允许直接 SQL 查询,但我只能使用 getResultList() 作为 List<Object> 获取那些,这实际上是 List<Object[]>List<Object>,具体取决于选择的列数(据我所知理解)并在将List<Object> 转换为List<Object[]> 时给出一些难看的警告。

我明白 JPA 通常不能提供 ResultSet 或 JDBCConnection,因为它可能是对文本或大数据或内存缓存而不是 SQL 的访问。但是我还没有找到类似 SQL-JPA 的东西。

在 JPA 应用程序中使用 EclipseLink 的直接和特定方法是错误且非常肮脏的。我可以确定应用程序将保留在 SQL 上,但不能确定它会保留在 EclipseLink 上。

所以看来我必须自己编写一个 ResultSet,其中包含列数和列名等元数据,并且我无法从数据库结果中读取列的数据类型。但是在 JPA 之外写一个 ResultSet 类来记忆 SQL-String 中的部分信息感觉也是不对的。

在 JPA-Application 中打开一个额外的 JDBC-Connection 似乎可以解决问题,但我什至没有从 JPA 中获取连接数据,而且两个单独的配置迟早会导致厄运。

是我的用例太奇怪还是 JPA 太有限或者我只是对正确的解决方案视而不见?

SQL是一种非常强大且简单的语言,用于复杂的数据关系,所以我想知道为什么 JPA 如此害怕它(当然,大量典型的 Object-Write 和 Object-Read用 jdbc 很痛苦)。

有谁知道 JPA 的现有 ResultSet-Implementation,或者有什么我看不到的正确解决方案?

感谢您的帮助。

【问题讨论】:

  • 然后想象最终用户一起点击了一些值,这些值被程序转换为动态 SQL 查询。数据请求通常比“我有一个对象person 带有姓名、城市和生日”更灵活一些,所以我没有固定对象。我可以将结果的列定义为对象或类,但我看不出有办法在一个 JPA 请求中读取不同的对象
  • 在您的应用程序中是否可以选择首先声明一个数据源并在您的 JPA 配置中使用该数据源(例如 jndi 数据源)?
  • @Serge,感谢您的提示。但是由于我在一个完整的应用程序中只有一个小角落,那会感觉很奇怪。我的印象是 JPA 应用程序中通常不涉及动态部分......但也许我在某些时候错了。如果我们要定义该 jdi 数据源,我们是否可以从 JPA 访问该数据源,还是必须将该数据源传递给整个程序?
  • 如果您有一个数据源,您可以让 JPA 使用它(并且您的所有应用程序将继续使用 JPA)并让特殊部分在 JDBC 级别使用它。这实际上是一个常见的优化用例:大部分代码使用 JPA 等 ORM,一些瓶颈部分使用 JDBC。
  • 所以 jdbc 连接是在“外部”JPA 处理的,不过也可以在 JPA 内部使用。我将与团队讨论,感谢您的提示。这意味着需要一个额外的通道来为我的类提供 JDBC 连接,而 JPA 只提供JPA.em()

标签: java jpa jdbc


【解决方案1】:

因为我只看过cmets,所以我会尽量提供一个答案,但是由于这个问题没有非常具体的编程问题,所以我的答案会是一样的。

JPA 是一个 ORM,这意味着它将数据库事物映射到 Java 对象,并提供一些围绕它的更多功能(事务、将 EntityManager 操作转换为 SQL 查询、命名查询等)。因为 ORM 从定义上说是 JDBC 之上的一层,所以它没有你需要的目的。

你是对的:

  • JPA 1.0 中没有从EntityManager 实例获取 JDBC 连接的标准方法,虽然它不是很好,但也没有你想象的那么脏:从 EntityManager 返回 JDBC 连接的单个静态方法,这将是唯一的脏地方。如果您将切换到 Hibernate,您只需更改几行。
  • 但在 JPA 2.0 中有一个 JPA 特定的方式:

    Connection conn = em.unwrap(Session.class).connection();

    您应该在 JPA 事务中以这种方式访问​​连接。否则,您将负责释放连接。 (引自here

或者使用常规 JPQL 查询来检索所有需要的数据,然后您将直接获得正确的类型。这里的限制是:它不像 SQL 那样灵活,但远非如此(例如,您可以使用 JPA 2.1 中的 JOIN ON)。

【讨论】:

  • 感谢您的结论。如果我将请求组合为 JPQL,我仍然需要一个将所有可能的结果字段作为成员的类(一个对象 = 一个数据行),并且我无法将列类型编写为类。包装提供程序特定代码的想法当然是个好主意,可能大多数大型 SQL 提供程序都具有这种连接访问权限。
  • @flaschenpost 我已经添加了关于如何在 JPA 2.0 中从 EntityManager 获取 JDBC 连接的重要细节。
  • 谢谢,我想我已经尝试过了,但可能是在我知道@Transactional 之前。我试试看。
  • Hibernate 的旧版本确实支持它(但可能更新)。有很多方法可以打开事务,它们之间是使用@Transactional,在这种情况下会自动为您打开一个事务(如果这是一个公共方法)。您是否尝试过使用 Hibernate 或 EclipseLink?
【解决方案2】:

如果我理解正确,您需要的是一个原生 SQL 查询(可以与 JPA 一起使用),然后将结果放入一些数据结构中进行操作,但无需事先在实体中映射,对吧?

那么Query Language Result Mapper (QLRM) 项目可能对您有用,因为它允许您从本机查询创建结果对象,唯一要求是参数具有相同的数量和类型:

Query q = em.createNativeQuery("SELECT ID, NAME FROM EMPLOYEE");
List<EmployeeTO> list = jpaResultMapper.list(q, EmployeeTO.class);

如果您真的对传入的结果一无所知,因为它们是通用的,那么您别无选择,只能使用普通 JDBC 并检查元数据。

但请记住,让用户建立自己的通用查询具有潜在危险,因为可能会发生 SQL injection 之类的事情。最好尝试定义一些可能的构建块(使用准备好的语句和绑定变量而不是字符串连接的 SQL),您可以单独测试并让用户从中选择。

【讨论】:

  • 几乎正确。我不知道在编译时将读取哪些字段,而只是像在 html 表或 csv 结果中一样显示它们。对于您的示例,我需要在编译时知道结果列,因为我必须使用数据类型定义 EmployeeTo。
猜你喜欢
  • 2017-04-06
  • 1970-01-01
  • 2020-03-15
  • 2020-07-31
  • 1970-01-01
  • 2015-10-22
  • 1970-01-01
  • 2015-01-27
  • 1970-01-01
相关资源
最近更新 更多