【问题标题】:Spring Hibernate Query One to One relationship with non primary keySpring Hibernate查询与非主键的一对一关系
【发布时间】:2020-01-02 09:51:22
【问题描述】:

我正在使用 Hibernate 在 Spring 上开发一个 Web 应用程序。

此网络应用程序基本上是一个在线商业网站,其中包含特定类别下的许多产品。

本应用支持多种语言为一个产品和不同尺寸的图片为一个产品图像,以便根据不同的用户设备进行优化。因此,用户可以根据自己的设备类型(如移动设备、iPad、台式机)选择他们希望以哪种语言查看产品描述,并获得不同尺寸的产品图像。

所以,我现在有六张桌子,如下所示。

产品表包含如图所示的产品

product_info 表包含一个产品的不同语言的相同描述,如英语、西班牙语和韩语,如下图所示。

product_image 包含产品同一张图片的不同尺寸图片的相对路径,如下图所示。

假设用户请求 category_id = 1(家具)、support_language_id = 1(英语)和 device_type_id = 1(移动设备)下的产品列表。然后,我想在 HQL 中查询类似以下内容。

Select * from product pr
left join product_info pf
on p.id = pf.product_id
left join product_image pi
on p.id = pi.product_id
where category_id = category_id of user choice
and support_language_id = support_language_id of user choice
and device_type_id = device_type_id of user device

当我执行这个 HQL 查询时,我希望根据用户选择的 category_id、support_language_id 和 device_type_id 获得一个产品对象列表,其中每个产品对象只有一个 product_info 对象和一个 product_image 对象。

按照上面的示例,我将获得一个 id = 1 的产品对象,因为只有一个 category_id = 1 的对象。然后,产品对象将具有 id = 1 的 product_info 对象和 id = 1 的 product_image 对象。

如何使用 Hibernate 在 JPA 中实现这种关系?

我已经尝试过这种方法,但最终会出现错误,例如与非主键或其他键绑定。所以我想知道我的数据库设计是否首先存在结构问题。

你们能给我一个完整的工作代码示例吗?请考虑所有这些 JPA 注释,如 @JoinColumn、@OneToMany、@OneToOne 等等?

您可以更改、添加和删除列或表,只要我能得到想要的结果。

【问题讨论】:

  • 这是不可能的。您不能限制映射集合中的实体。
  • @Simon Martinelli 那么,我应该重新设计我的数据库关系吗?

标签: java mysql spring hibernate jpa


【解决方案1】:

很简单,假设您没有 10 年前的 JPA 版本,那么您可以创建两个接口(在本例中为存储库)并按照 JPA 命名约定命名方法。 这意味着,如果您有这样定义的类 ProductInfo:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProductInfo {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "product_info_id")
    private long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "product_id")
    private Product product;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "language_id")
    private Language language;

    private String description;

}

这将是您需要的相应的 Repository 方法,是的,如果您想让它干净和简单,您需要传递整个 Product。

 @Repository
 public interface ProductImageRepository extends JpaRepository<ProductImage, Long> {

    public Optional<ProductImage> findByProductAndDevice(Product product, Device device);

}

ProductImage 也是如此

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProductImage {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "product_image_id")
    private long id;

    @ManyToOne
    @JoinColumn(name = "product_id")
    private Product product;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "device_id")
    private Device device;

    private String image;

}

谁的存储库方法将是

@Repository
public interface ProductImageRepository extends JpaRepository<ProductImage, Long> {

    public Optional<ProductImage> findByProductAndDevice(Product product, Device device);

}

那么你会想要一些包含产品信息的东西

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductToDisplay {

    private Product product;
    private String description;
    private String imageLocation;
}

然后根据您拥有的信息创建该对象的服务

@Service
public class ProductToDisplayService {

    @Autowired
    private ProductInfoRepository productInfoRepository;
    @Autowired
    private ProductImageRepository productImageRepository;

    @Autowired
    private ProductRepository productRepository;
    @Autowired
    private DeviceRepository deviceRepository;
    @Autowired
    private LanguageRepository languageRepository;

    public ProductToDisplay getProduct(long productID, long languageID, long deviceID) throws Exception {
        Product product =
                productRepository.findById(productID).orElseThrow(()-> new Exception(
                        "There is no such product"));

        return new ProductToDisplay(product,
                productInfoRepository.findByProductAndLanguage(product,
                        languageRepository.findById(languageID).orElseThrow(()-> new Exception(
                                "There is no such product"))).orElseThrow(()-> new Exception(
                        "There is no such product info")).getDescription(),
                productImageRepository.findByProductAndDevice(product,
                        deviceRepository.findById(deviceID).orElseThrow(()-> new Exception(
                                "There is no such product"))).orElseThrow(()-> new Exception(
                        "There is no such product Image")).getImage());
    }
}

这里可能看起来有点混乱,对此我很抱歉,但这更清楚一点

public ProductToDisplay getProductClear(long productID, long languageID,
                                     long deviceID) throws Exception {
    Product product =
            productRepository.findById(productID).orElseThrow(()-> new Exception(
                    "There is no such product"));

    Language language = languageRepository.findById(languageID).orElseThrow(()-> new Exception(
            "There is no such language"));
    ProductInfo prodInfo = productInfoRepository.findByProductAndLanguage(product, language)
            .orElseThrow(()-> new Exception(
            "There is no such product info"));

    Device device =  deviceRepository.findById(deviceID).orElseThrow(()-> new Exception(
            "There is no such image"));
    ProductImage prodImg = productImageRepository.findByProductAndDevice(product, device)
            .orElseThrow(()-> new Exception(
            "There is no such product Image"));

    return new ProductToDisplay(product,
            prodInfo.getDescription(), prodImg.getImage());
}

【讨论】:

  • 非常感谢您的建议,但我一直在寻找一个解决方案,其中单个查询通过结构良好的 JPA 关系完成我需要的操作,而不是查询每个表,然后将它们放在一起结束。不过无论如何,非常感谢您的建议!!
  • @kisungTae 您需要的问题是您只需要几个字段而不是整个对象。不管怎样,永远不要用 Hibernate 做太长的查询,这很不方便!这样更容易维护,也更容易发现应用程序将返回的错误。另外,您已经准备好用作 DTO 的对象。考虑到您不必编写一个查询,我强烈建议您使用这种方法。
【解决方案2】:

这里是一个不使用注解,只使用hql的解决方案,不知道会不会让你满意。

select p from Product p,ProductInfo pf,ProductImage pi where p.id = pf.productId and p.id = pi.productId and...

【讨论】:

  • 非常感谢,但我不认为这对我有用
猜你喜欢
  • 1970-01-01
  • 2018-08-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多