SpringBoot JPA查詢結果映射到自定義實體類


場景

舉一個簡單的例子:

比如有一個Position實體類

@Entity
@Table(name = "position")
public class Position implements Serializable {
    private static final long serialVersionUID = 768016840645708589L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private BigDecimal salary;
    private String city;
    
    ...//省略getset方法
    ...//省略toString方法    
}    

然后有一個PositionDetail實體類

@Entity
@Table(name = "position_detail")
public class PositionDetail implements Serializable {

    private static final long serialVersionUID = 4556211410083251360L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Long pid;

    private String description;
    
    ...//省略getset方法
    ...//省略toString方法    
}    

需求:查詢職位基本信息,職位描述,因為涉及到兩張表操作,簡單的查詢並不能滿足我們的需求,因此就需要自定義查詢接口並返回符合需求的結果。

接下來再定義一個實體類,用來接收查詢結果

public class PositionDO {
    private Long id;
    private String name;
    private BigDecimal salary;
    private String city;
    private String description;

    public PositionDO(Long id, String name, BigDecimal salary, String city, String description) {
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.city = city;
        this.description = description;
    }

    ...//省略getset方法
    ...//省略toString方法  
}

編寫Dao接口,用來實現CRUD操作

public interface PositionDao extends JpaRepository<Position, Long> {

    @Query(nativeQuery = true, value = "select t1.id, t1.name, t1.salary, t1.city, t2.description) \n" +
        "from position t1 left join position_detail t2 on t1.id = t2.pid \n" +
        "where t1.id = :id")
    PositionDO findPositionById(@Param("id") Long id);
}

思考:如果這樣寫會不會出現問題?接下來我們編寫一個測試類測試一下。

@SpringBootTest(classes = ShardingApplication.class)
@RunWith(SpringRunner.class)
public class TestShardingDatabase {

    @Resource
    PositionDao positionDao;

    @Test
    public void testQueryById() throws Exception{
        PositionDO positionDO = positionDao.findPositionById(548083053407240192L);
        System.out.println(positionDO);
    }
}

哈哈,翻車了吧,還好先測試了一波,問題不大。

Hibernate: select t1.id, t1.name, t1.salary, t1.city, t2.description 
from position t1 left join position_detail t2 on t1.id = t2.pid 
where t1.id = ?

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.lsp.dto.PositionDO]

分析原因:那么到底是為什么造成這樣的原因呢?相信小伙伴們一眼就能看出來了,是因為Jpa找不到能夠從類型轉換的轉換器,而拋出這樣的異常。

現在問題來了,既然這樣不行,那么我們想要實現映射到自定義結果集該如何實現呢?

實現:JPA可以自定義SQL語句進行查詢,然后查詢語句可以通過原生SQL語句(原生SQL語句也就是在@Query注解里加上nativeQuery = true)進行查詢。當然了,也可以通過JPQL進行查詢。

我們這里就是通過JPQL進行查詢,它的特征就是與原生SQL語句類似,完全面向對象,通過類名和屬性訪問,而不是表名和表屬性。

由此PositionDao修改之后就像這樣

public interface PositionDao extends JpaRepository<Position, Long> {

    @Query(value = "select new com.lsp.domain.PositionDO(t1.id, t1.name, t1.salary, t1.city, t2.description) \n" +
            "from Position t1 left join PositionDetail t2 on t1.id = t2.pid \n" +
            "where t1.id = :id")
    PositionDO findPositionById(@Param("id") Long id);
}

接下來我們再運行測試方法看一下。

Hibernate: select position0_.id as col_0_0_, position0_.name as col_1_0_, position0_.salary as col_2_0_, position0_.city as col_3_0_, positionde1_.description as col_4_0_ from position position0_ left outer join position_detail positionde1_ on (position0_.id=positionde1_.pid) where position0_.id=?
PositionDO{id=548083053407240192, name='Jerry5', salary=10000.00, city='beijing5', description='this is message 5'}

ok了,結果已經正確查詢出來。

總結: 注意上面的SQL語句是面向對象的,對應的字段也都是實體類里面的屬性。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM