【问题标题】:How to convert Object in to entity in JPA如何在 JPA 中将对象转换为实体
【发布时间】:2021-07-02 10:13:20
【问题描述】:

如何在 JPA 中将 Object 转换为实体

我有一个 tqo 表要加入,我已经完成了,我也得到了响应。但问题就在这里,我将其作为数组列表。我希望这是我的实体列表。

这里是仓库

package com.overflow.overflow.service;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import com.overflow.overflow.models.Transictions;

@Repository
public interface TransictionRepository extends JpaRepository<Transictions, Long> {
    @Query(nativeQuery = true,
            value = "SELECT transiction.user_id, transiction.quantity,transiction.instrument_name, transiction.Price,instrument.LTP"
            + "FROM instrument"
            + "INNER JOIN transiction"
            + "ON instrument.instrument=transiction.instrument_name")
    public List<Object[]> getTransictionsAndInstruments();
}

这是我的控制器

  @GetMapping("/getTransictionsAndInstruments") 
      public List<Object[]> getTransitionInstrument(){
         return transictionrepository.getTransictionsAndInstruments(); 
      }

任何机构都可以帮助我如何做到这一点。我已经为使用findAll() 示例的一台服务器完成了此操作。

@GetMapping("/getTransictionData")
    public List<Transictions> getAllTransiction(){
        return transictionrepository.findAll();
        //return transictionrepository.getTransictionsAndInstruments();
    }

乐器

package com.springboot.Ole.Model;



import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "instrument")
public class Instrument {
    
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    @Column(name = "instrument")
    private String instrumentName;
    
    @Column(name = "ltp")
    private float LTP;
    
    @Column(name = "exchange")
    private String exchange;
    
    @Column(name = "joCaps")
    private String joCaps;
    
    
    
      @OneToMany(mappedBy = "instrument", fetch = FetchType.LAZY)
      private List<Transictions> transiction;
     
    
    

    public Instrument(String instrumentName, float lTP, String exchange, String joCaps) {
        super();
        this.instrumentName = instrumentName;
        LTP = lTP;
        this.exchange = exchange;
        
    }
    
    public Instrument () {
        
    }
    
    
        // Getter Setter

过渡

package com.springboot.Ole.Model;

import java.sql.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;


@Entity
@Table(name = "transiction")
public class Transictions {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long Id;
    
    @Column(name = "userId")
    private Long userId;
    
    @Column(name = "instrumentName")
    private String InstrumentName;
    
    @Column(name = "quantity")
    private int quantity;
    
    @Column(name = "price")
    private double price;
    
    @Column(name = "transictionDate")
    private Date transictionDate;
    
    
     @ManyToOne(fetch = FetchType.EAGER)
      
     @JoinColumn(name = "ltp") 
      private Instrument instrument;
     
    
    public Transictions() {
    }
    
    public Transictions(Long userId, String instrumentName, int quantity, double price, Date transictionDate) {
        super();
        this.userId = userId;
        InstrumentName = instrumentName;
        this.quantity = quantity;
        this.price = price;
        this.transictionDate = transictionDate;
    }
    
    // Getter Setter

【问题讨论】:

  • 你能添加类 Transicions 吗?我认为问题来自您请求中的属性名称。尝试添加别名(例如 transiction.user_id as user_id)

标签: java spring spring-boot jpa spring-data-jpa


【解决方案1】:

使用本机查询,您有两种解决方案,将数据提取到自定义对象或接口中。

自定义对象

如果您想将本机查询的结果提取到不是实体的自定义对象中,您可以使用@SqlResultSetMapping

创建您的自定义对象:

@Getter
@Setter
@AllArgsConstructor
public class CustomTransiction {

    private long userId;
    private int quantity;
    private String instrumentName;
    private double price;
    private float ltp;

}

使用映射信息 (@SqlResultSetMapping) 定义一个命名的本机查询 (@NamedNativeQuery),以便它可以将结果提取到您的自定义对象中:

@NamedNativeQuery(
        name = "findTransictions",
        query = """
            SELECT transiction.user_id, transiction.quantity, transiction.instrument_name, transiction.Price, instrument.LTP
            FROM instrument
            INNER JOIN transiction
            ON instrument.instrument = transiction.instrument_name
        """,
        resultSetMapping = "transictions"
)
@SqlResultSetMapping(name = "transictions",
        classes = {
                @ConstructorResult(targetClass = CustomTransiction.class, columns = {
                        @ColumnResult(name = "userId", type = Long.class),
                        @ColumnResult(name = "quantity", type = Integer.class),
                        @ColumnResult(name = "instrucmentName", type = String.class),
                        @ColumnResult(name = "price", type = Double.class),
                        @ColumnResult(name = "ltp", type = Float.class)
                })
        }
)

然后像这样在存储库中使用您命名的本机查询:

@Query(name = "findTransictions", nativeQuery = true)
List<CustomTransiction> getTransictionsAndInstruments();

界面投影

如果仅用于显示目的,更简单的方法是使用界面投影:

创建一个接口来保存您的数据:

public interface TransictionProjection {

    Long getUserId();
    Integer getQuantity();
    String getInstrumentName();
    Double getPrice();
    Float getLTP();

}

然后您可以直接从您的存储库中使用它:

@Query(value = """
            SELECT transiction.user_id, transiction.quantity, transiction.instrument_name, transiction.Price, instrument.LTP
            FROM instrument
            INNER JOIN transiction
            ON instrument.instrument = transiction.instrument_name""", nativeQuery = true)
List<TransictionProjection> getTransictionsAndInstruments();

请注意,列名必须与接口方法名匹配,因此您可能需要在查询中使用用户别名,即SELECT transiction.user_id as userId, transiction.quantity, transiction.instrument_name as instrumentName, ...

编辑

评论中问题的一些解释:

实际上,您有许多使用 JPA 获取数据的解决方案,这完全取决于您的应用程序复杂性以及您对数据的处理方式。

JPQL 进入实体

常见的方法是使用 JPQL(无原生查询)直接获取 JPA 实体(@Entity 注释)。

例如:@Query("select t from Transiction t where ...") 它必须返回实体 (Transiction)。

问题在于它会获取所有数据,包括嵌套关系(取决于渴望/惰性关联),并且取决于您的实体复杂性,这可能是巨大的性能杀手。当您查看生成的 SQL 时,这可能会很痛苦……您也可能面临 N+1 问题。当然,您可以使用fetch joins 等来调整它,但没有什么神奇的。

但是,如果您以后需要更新数据,这是必须的,因为如果您想做save(),您必须获得完整的实体。或者您可以使用带有@Modifying 的更新查询,但这并不总是适用(例如,这有助于避免获取整个实体以更新状态)。

JPQL 到自定义对象

如果您只需要实体中的一些数据(不是全部),您可以使用带有构造函数的自定义模型类,以便您可以从 SELECT 查询中调用它。它不需要是@Entity

例如:@Query("select new com.example.CustomTransiction(id, userId, price) from Transiction t where ...") 它必须返回自定义对象 (CustomTransiction)。

但它仍然是 JPQL,并且 JPA 仍然可以根据您的实体复杂性生成一些奇怪的查询。它不能防止 N+1 问题等。

此外,您不能使用该对象来更新数据,您可能仍需要稍后获取整个实体。这对于显示目的很有用。

带有界面投影的本机查询

在这种情况下,您使用本机查询 (nativeQuery = true),因此您可以控制查询和 SQL 性能。

例如:@Query("select id, user_id as userId, price from transiction t where ...", nativeQuery = true) 它必须返回接口(例如TransictionProjection)并且所有字段必须匹配。

这是最简单和最高效的解决方案,但这仅在我们使用界面时用于显示目的。

您仍然需要获取实体来更新数据。

对自定义对象的本机查询

您仍然使用本机查询,但您将结果投影到自定义类中,而不是接口中。这可以使用上面的@NamedNativeQuery@SqlResultSetMapping 来完成。与界面不同的是,您可以更新您的对象(例如更新 UI)。但是您仍然需要实体来更新数据库中的数据。

原生查询的坏处是您失去了一些 ORM 的优势(不再与数据库无关,您更依赖于您的数据库管理系统)。老实说,即使只使用JPQL,在切换数据库系统时可能仍然需要一些修改(即保留字,PostgreSQL中的空参数管理等),所以我个人认为如果性能提高很多,这没什么大不了的并降低应用程序的复杂性。

【讨论】:

  • 我已经尝试过但收到此错误。我通过使用 Transiction 投影来使用接口逻辑“org.springframework.http.converter.HttpMessageNotWritableException:无法编写 JSON:来自建议的空返回值与原始返回类型不匹配:public abstract long com.springboot.Ole.repository.TransictionProjection .getUserId();"
  • @NamedNativeQuery@SqlResultSetMapping o 我需要在存储库或自定义对象类中写入..
  • 您可以将注释放在您的实体类中。对于接口投影,列的名称必须与接口方法的名称匹配,因此您可能需要在查询中使用别名,即SELECT transiction.user_id as useriD, transiction.quantity, transiction.instrument_name as instrumentName, transiction.Price as price, instrument.LTP as LTP FROM ...。如果部分数据可以为null,接口中不要使用原始类型,使用Long getUserId();Integer getQuantity();等。
  • 我刚刚更改了 Integer 并且它有效。谢谢。我对这个问题的看法是“如果我正在编写任何特定的查询,我需要创建单独的类/接口来获得我想要的自定义结果”。如果我错了,请纠正我。
  • 是的,如果需要获取自定义结果,不代表实体,需要使用接口或者自定义对象类。根据我的个人经验,我添加了一些关于 JPA 不同解决方案的更多解释。您可以根据自己的需要选择合适的。
【解决方案2】:

您需要在要返回的实体类中创建构造函数,然后在选择部分使用该构造函数。

    @Query("select new com.PACKAGE_NAME.YOUR_ENTITY_NAME(transiction.user_id, transiction.instrument_name, transiction.Price,instrument.LTP) from YOUR_ENTITY_NAME transiction")
List<Your Entity Name> getTransictionsAndInstruments();

在你的情况下,它看起来像这样:

    @Query("select new com.springboot.Ole.Model.Transictions(transiction.user_id, transiction.instrument_name, transiction.Price,instrument.LTP) from Transictions transiction")
    List<Transictions> getTransictionsAndInstruments();

【讨论】:

    【解决方案3】:

    将此添加到您的 Transiction Entity 模型中。

      @ManyToOne
      @JoinColumn(name = "instrument", referencedColumnName = "instrumentName") 
      private Instrument instrument;
    

    那么这将为您完成这项工作

    @GetMapping("/getTransictionData")
        public List<Transictions> getAllTransiction(){
            return transictionrepository.findAll();
        }
    

    为什么要把事情复杂化?

    只是您想根据仪器名称将它们加入到表中。

    【讨论】:

    • 感谢您的回答。我在更新仪器和转换模型的同时遇到了一些错误。请看一看。
    • 我已经从转换中删除了@ManyToOne(fetch = FetchType.EAGER),然后清理了存储库类。我已经在使用您编写的控制器方法,但没有获得 ltp 的值。通过执行前面的代码,我得到了 ltp 作为数组的响应,而不是键值对
    • 也查询,因为它会根据各自的仪器名称加入。
    • @David 检查更新的答案。如果您正确修改 @JoinColumn,那么 JPA 将能够根据 instrumentName 加入这两个实体,然后 findAll 也将正确返回工具
    • 哦,好的,在收到该行后,我收到一些“创建类路径资源中定义的名称为 'entityManagerFactory' 的 bean 时出错”错误。让我看看那是什么。
    猜你喜欢
    • 2016-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多