【问题标题】:How to return a Map where the key is an entity property and the value is the entity with JPA and Hibernate如何返回一个映射,其中键是实体属性,值是具有 JPA 和 Hibernate 的实体
【发布时间】:2020-04-10 18:41:51
【问题描述】:

比如说我有一个 sql 表

create table CUSTOMER
(
  first_name      VARCHAR2(30) not null,
  last_name       VARCHAR2(30) not null,
  age             VARCHAR2(30) not null,
  credit_card_num VARCHAR2(30) not null UNIQUE
);

我想获得一个 Java Map,其中映射键的值是 CUSTOMER.credit_card_num 并且地图值是CUSTOMER的所有属性 比如:

1234134cardnumber -> Customer(first_name: Jack, age: 23, credit_card_num: 1234134cardnumber),
123faef -> Customer(first_name: Mike, age: 43, credit_card_num: 123faef)

等等……

我会接受在 JPA 或 Native Query 中的任何方式

更新

如https://stackoverflow.com/users/1025118/vlad-mihalcea所指,可以由https://vladmihalcea.com/why-you-should-use-the-hibernate-resulttransformer-to-customize-result-set-mappings/完成

【问题讨论】:

  • 见this
  • @SternK 所以如果没有for 循环,就不可能得到Map<String, Customer> 或Map<String, Map<String, String>>。我们能得到的最好的东西是List<Map<String, String>>
  • 您只能通过返回List (JPA 1.0) 的Query.getResultList() 或返回List<X> (JPA 2.0) 的TypedQuery<X>.getResultList() 或'TypedQuery.getResultStream() 获取查询结果' 返回 Stream<X> (JPA 2.2)。所以,没有办法以Map获取查询结果。

标签: java sql hibernate dictionary jpa


【解决方案1】:

这个问题有两种解决方案。

假设您正在使用以下Post 实体:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    private Long id;

    private String title;

    @Column(name = "created_on")
    private LocalDate createdOn;

    public Long getId() {
        return id;
    }

    public Post setId(Long id) {
        this.id = id;
        return this;
    }

    public String getTitle() {
        return title;
    }

    public Post setTitle(String title) {
        this.title = title;
        return this;
    }

    public LocalDate getCreatedOn() {
        return createdOn;
    }

    public Post setCreatedOn(LocalDate createdOn) {
        this.createdOn = createdOn;
        return this;
    }
}

您的数据库中保留了以下实体:

entityManager.persist(
    new Post()
        .setId(1L)
        .setTitle("High-Performance Java Persistence eBook has been released!")
        .setCreatedOn(LocalDate.of(2016, 8, 30))
);

entityManager.persist(
    new Post()
        .setId(2L)
        .setTitle("High-Performance Java Persistence paperback has been released!")
        .setCreatedOn(LocalDate.of(2016, 10, 12))
);

entityManager.persist(
    new Post()
        .setId(3L)
        .setTitle("High-Performance Java Persistence Mach 1 video course has been released!")
        .setCreatedOn(LocalDate.of(2018, 1, 30))
);

entityManager.persist(
    new Post()
        .setId(4L)
        .setTitle("High-Performance Java Persistence Mach 2 video course has been released!")
        .setCreatedOn(LocalDate.of(2018, 5, 8))
);

entityManager.persist(
    new Post()
        .setId(5L)
        .setTitle("Hypersistence Optimizer has been released!")
        .setCreatedOn(LocalDate.of(2019, 3, 19))
);

使用 Java 8 流

按照您的问题要求返回Map 的方法如下:

Map<Long, Post> postByIdMap = entityManager
.createQuery(
    "select p " +
    "from Post p ", Post.class)
.getResultStream()
.collect(
    Collectors.toMap(
        Post::getId,
        Function.identity()
    )
);

assertEquals(
    "High-Performance Java Persistence eBook has been released!",
    postByIdMap.get(1L).getTitle()
);

assertEquals(
    "Hypersistence Optimizer has been released!",
    postByIdMap.get(5L).getTitle()
);

使用休眠ResultTransformer

使用ResultTransformer 可以实现相同的目标:

Map<Long, Post> postByIdMap = (Map<Long, Post>) entityManager
.createQuery(
    "select p " +
    "from Post p ")
.unwrap(org.hibernate.query.Query.class)
.setResultTransformer(
    new ResultTransformer() {

        Map<Long, Post> result = new HashMap<>();

        @Override
        public Object transformTuple(Object[] tuple, String[] aliases) {
            Post post = (Post) tuple[0];
            result.put(
                post.getId(),
                post
            );
            return tuple;
        }

        @Override
        public List transformList(List collection) {
            return Collections.singletonList(result);
        }
    }
)
.getSingleResult();

assertEquals(
    "High-Performance Java Persistence eBook has been released!",
    postByIdMap.get(1L).getTitle()
);

assertEquals(
    "Hypersistence Optimizer has been released!",
    postByIdMap.get(5L).getTitle()
);

【讨论】:

    【解决方案2】:

    我见过的最好的方法是使用 Spring 存储库

    public interface CustomerRepository extends CrudRepository<Customer, Long> {
    
        List<Customer> findAll();
    
        default Map<Long,Customer> findMapById() {
            return findAll().stream().collect(Collectors.toMap(Customer::getId,
                    Function.identity()));
        }
    
        default Map<String,Customer> findMapByCreditcard() {
            return findAll().stream().collect(Collectors.toMap(Customer::getCreditCardNum,
                    Function.identity()));
        }
    }
    

    但返回地图的不是 JPA 本身

    【讨论】:

    • 我想避免将 Java 代码中的 List 转换为 Map。我认为从单个选择结果Customer(first_name: Jack, age: 23, credit_card_num: 1234134cardnumber) 我们有足够的信息来制作地图和查询应该能够做到这一点
    • JPA 框架不会返回@SternK 提到的地图。但是您可以回退到普通的 JDBC 并自己处理结果集,并为结果集的每条记录创建一个映射条目。
    猜你喜欢
    • 2012-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-06
    • 1970-01-01
    • 1970-01-01
    • 2013-12-24
    • 1970-01-01
    相关资源
    最近更新 更多