【问题标题】:Hibernate OneToMany, fetch with custom queryHibernate OneToMany,使用自定义查询获取
【发布时间】:2021-01-20 11:21:36
【问题描述】:

我有几个实体,这些是休眠映射的。菜单、菜单详细信息、语言。当菜单存在时,该实体最重要的属性是它的 id。有几种语言的 id 为“nl”、“en”、“fr”。例如,当用户使用 3 种语言时,他们可以在菜单内的 OneToMany 列表中创建具有 3 个 MenuDetails 的菜单。当我尝试通过 id 获取菜单时,我得到了所有的 MenuDetails。但是,我如何能够获取列表中只有一个 MenuDetails 实体的菜单,例如 id 'nl'?有人能帮我吗。现在我手动进行关联,但这需要很多网络和 CPU 资源。

@Entity
@Table(name = "menus")
public class Menu {
    @Id
    @Column(length = 36, insertable = false, updatable = false, nullable = false)
    private String id = UUID.randomUUID().toString();

    private String shopId;

    private Integer sequence;

    private Character status;

    @Transient
    private List<MenuDetails> menuDetails;

    @Transient
    private List<Category> categories;

    public Menu setId(String id) {
        this.id = id;
        return this;
    }

    public String getId() {
        return id;
    }

    public Menu setShopId(String shopId) {
        this.shopId = shopId;
        return this;
    }

    public String getShopId() {
        return shopId;
    }

    public Menu setMenuDetails(List<MenuDetails> menuDetails) {
        this.menuDetails = menuDetails;
        return this;
    }

    public List<MenuDetails> getMenuDetails() {
        return menuDetails;
    }

    public Menu setCategories(List<Category> categories) {
        this.categories = categories;
        return this;
    }

    public List<Category> getCategories() {
        return categories;
    }

    public Integer getSequence() {
        return sequence;
    }

    public Menu setSequence(Integer sequence) {
        this.sequence = sequence;
        return this;
    }

    public Character getStatus() {
        return status;
    }

    public Menu setStatus(Character status) {
        this.status = status;
        return this;
    }
}
@Entity
@IdClass(MenuDetailsId.class)
@Table(name = "menu_details")
public class MenuDetails {
    @Id
    private String menuId;

    @Id
    private String languageCode;

    private String name;

    // Empty constructor required for composite primary key
    public MenuDetails(){ super(); }

    public String getMenuId() { return menuId; }
    public MenuDetails setMenuId(String menuId) {
        this.menuId = menuId;
        return this;
    }

    public String getLanguageCode() { return languageCode; }
    public MenuDetails setLanguageCode(String languageCode) {
        this.languageCode = languageCode;
        return this;
    }

    public String getName() { return name; }
    public MenuDetails setName(String name) {
        this.name = name;
        return this;
    }

    public static MenuDetails createEmpty(String menuId, String languageCode) {
        return new MenuDetails()
                .setMenuId(menuId)
                .setLanguageCode(languageCode)
                .setName("");
    }
}
@Entity
@Table(name = "languages")
public class Language {
    @Id
    @Column(length = 36, updatable = false, nullable = false)
    private String id;

    @Column(length = 5)
    private String code;
    private String name;
    private String iconUrl;

    public Language setId(String id){
        this.id = id;
        return this;
    }
    public String getId(){ return id; }

    public Language setCode(String code){
        this.code = code;
        return this;
    }
    public String getCode(){ return code; }

    public Language setName(String name){
        this.name = name;
        return this;
    }
    public String getName(){ return name; }

    public Language setIconUrl(String iconUrl){
        this.iconUrl = iconUrl;
        return this;
    }
    public String getIconUrl(){ return iconUrl; }
}

【问题讨论】:

  • 您能分享您当前的方法和相关实体吗?
  • code @Entity @Table(name = "menus") public class Menu { @Id @Column(length = 36, insertable = false, updatable = false, nullable = false) private String id = UUID.randomUUID().toString();私人字符串 shopId;私有整数序列;私人角色状态; @Transient 私有 List menuDetails; @Transient 私有 List 类别; code
  • code @Entity @IdClass(MenuDetailsId.class) @Table(name = "menu_details") public class MenuDetails { @Id private String menuId; @Id 私有字符串语言代码;私有字符串名称;
  • code @Entity @Table(name = "languages") public class Language { @Id @Column(length = 36, updatable = false, nullable = false) private String id; @Column(length = 5) 私有字符串代码;私有字符串名称;私有字符串 iconUrl;
  • 还有一些实体,例如,categories,category_details,products,product_details,supplements,condiments,allergens,zones,tables,...

标签: java sql spring-boot hibernate one-to-many


【解决方案1】:

如果您使用连接提取,那么您就不走运了。 JPA 要求托管实体和数据库的状态必须在事务结束时保持同步,因此仅获取实体的部分集合实际上会删除未获取的元素,这就是 JPA 不允许这样做的原因。但是,您可以使用 Hibernate 执行此操作,但我不推荐它,因为它可能导致删除。

因此,要获得您想要的,您需要编写 HQL 或 JPA Criteria 查询并将数据提取到 DTO 中。在查询中,您选择所需的所有字段,并可以定义您提到的连接条件。我认为这是Blaze-Persistence Entity Views 的完美用例。

我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您按照自己喜欢的方式定义目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。

使用 Blaze-Persistence Entity-Views 的用例的 DTO 模型可能如下所示:

@EntityView(Menu.class)
public interface MenuDto {
    @IdMapping
    String getId();
    String getShopId();
    Integer getSequence();
    Character getCharacter();
    @Mapping("menuDetails[languageCode = 'nl']") // Could also use a parameter e.g. :language
    MenuDetailsDto getMenu();

    @EntityView(MenuDetails.class)
    interface MenuDetailsDto {
        @IdMapping
        String getId();
        String getName();
    }

    // Other mappings
}

查询是将实体视图应用于查询的问题,最简单的就是通过 id 进行查询。

MenuDto a = entityViewManager.find(entityManager, MenuDto.class, id);

Spring Data 集成让您可以像使用 Spring Data Projections 一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

唯一的“问题”是,不完全支持 id 类,但您可以使用可嵌入类来代替复合 id。

【讨论】:

    猜你喜欢
    • 2016-04-24
    • 2012-11-15
    • 2019-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 2012-12-22
    • 1970-01-01
    相关资源
    最近更新 更多