【问题标题】:@OneToOne mapping for non key columns非键列的@OneToOne 映射
【发布时间】:2020-03-18 01:10:14
【问题描述】:

我正在将 Hibernate 用于现有的旧架构(我无法更新它)并且有一个奇怪的案例:

表用户:

  1. db_id PK
  2. user_id(唯一约束)
  3. ...其他栏目

表格地址

  1. db_id PK
  2. user_id(唯一约束)
  3. ...常用地址列

它们具有一对一的关系。我正在创建以下 @Entity 注释类。

class UserEntity {
    @Id
    @GeneratedValue
    private UUID dbId;

    private String userId;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "user_id", referencedColumnName = "user_id")
    private AddressEntity address;
}
class AddressEntity {
    @Id
    @GeneratedValue
    private UUID dbId;

    private String userId;

    @OneToOne
    private UserEntity user;
}

Hibernate 认为这是一个重复的列定义,这是合理的。但是,由于在地址表中我没有 user_id 作为 FK 或 PK 我不确定如何将其传达给 Hibernate。我已尝试搜索类似但没有成功,因此非常感谢任何建议。

【问题讨论】:

    标签: hibernate spring-data-jpa spring-data hibernate-mapping


    【解决方案1】:

    更新

    可能有更好的方法来实现这一点,比如使用@NaturalId,但我无法让它与 Hibernate 5.2.12.Final 一起使用。

    但是,@JoinFormula 来救援:

    @Entity
    @Table(name = "T_USER")
    public class UserEntity implements Serializable
    {
        private static final long serialVersionUID = 1L;
    
        @Id
        @Type(type = "uuid-char")
        @Column(name = "DB_ID", length = 36)
        private UUID dbId;
    
        @Column(name = "USER_ID", nullable = false, unique = true)
        private String userId;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinFormula("(select x.DB_ID from T_ADDRESS x where x.USER_ID=USER_ID)")
        private AddressEntity address;
    
        @Override
        public String toString()
        {
            return String.format("%s: dbId=%s, userId=%s, address=%s",
                getClass().getSimpleName(),
                dbId,
                userId,
                address != null ? address.getDbId() : null);
        }
    }
    
    @Entity
    @Table(name = "T_ADDRESS")
    public class AddressEntity implements Serializable
    {
        private static final long serialVersionUID = 1L;
    
        @Id
        @Type(type = "uuid-char")
        @Column(name = "DB_ID", length = 36, nullable = false, unique = true)
        private UUID dbId;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")
        private UserEntity user;
    
        @Override
        public String toString()
        {
            return String.format("%s: dbId=%s, user=%s",
                getClass().getSimpleName(),
                dbId,
                user != null ? user.getDbId() : null);
        }
    }
    

    使用 MySQL8 对其进行了测试:

    CREATE TABLE `t_user` (
      `DB_ID` varchar(36) NOT NULL,
      `USER_ID` varchar(255) NOT NULL,
      PRIMARY KEY (`DB_ID`),
      UNIQUE KEY `UK_kvueux8cmkdekeqhrs7pumkwi` (`USER_ID`)
    ) ENGINE=InnoDB
    
    CREATE TABLE `t_address` (
      `DB_ID` varchar(36) NOT NULL,
      `USER_ID` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`DB_ID`),
      KEY `FK1s9gxk3we3yq11hjw5hp7ahp5` (`USER_ID`),
      CONSTRAINT `FK1s9gxk3we3yq11hjw5hp7ahp5` FOREIGN KEY (`USER_ID`) REFERENCES `t_user` (`user_id`)
    ) ENGINE=InnoDB
    

    使用这个快速的'n'dirty 启动器:

    public class Main
    {
        public static void main(String[] args)
        {
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("test_hibernate");
            try
            {
                EntityManager em = emf.createEntityManager();
                try
                {
                    EntityTransaction transaction = em.getTransaction();
                    transaction.begin();
    
                    try
                    {
                        UserEntity user = new UserEntity();
                        user.setDbId(UUID.randomUUID());
                        user.setUserId("user_" + System.nanoTime());
    
                        em.persist(user);
    
    
                        AddressEntity address = new AddressEntity();
                        address.setDbId(UUID.randomUUID());
    
                        address.setUser(user);
                        user.setAddress(address);
    
                        em.persist(address);
    
                        transaction.commit();
    
                        System.out.println("persisted user: " + user);
                        System.out.println("persisted address: " + address);
                    }
                    catch(Exception e)
                    {
                        e.printStackTrace();
                        transaction.rollback();
                    }
                }
                finally
                {
                    em.close();
                }
    
    
                EntityManager em2 = emf.createEntityManager();
                try
                {
                    List<UserEntity> userList = em2.createQuery("select x from UserEntity x", UserEntity.class).getResultList();
                    userList.forEach(x -> System.out.println("loaded user: " + x));
    
                    List<AddressEntity> addressList = em2.createQuery("select x from AddressEntity x", AddressEntity.class).getResultList();
                    addressList.forEach(x -> System.out.println("loaded address: " + x));
                }
                finally
                {
                    em2.close();
                }
            }
            finally
            {
                emf.close();
            }
    
            System.out.println("ok");
        }
    }
    

    产生了这些结果:

    persisted user: UserEntity: dbId=a22db668-eda0-4de1-83ae-98a7cd8738bd, userId=user_789235935853200, address=c17c0c28-603e-4961-90b0-16232346e47b
    persisted address: AddressEntity: dbId=c17c0c28-603e-4961-90b0-16232346e47b, user=a22db668-eda0-4de1-83ae-98a7cd8738bd
    
    loaded user: UserEntity: dbId=a22db668-eda0-4de1-83ae-98a7cd8738bd, userId=user_789235935853200, address=c17c0c28-603e-4961-90b0-16232346e47b
    loaded address: AddressEntity: dbId=c17c0c28-603e-4961-90b0-16232346e47b, user=a22db668-eda0-4de1-83ae-98a7cd8738bd
    

    【讨论】:

    • 谢谢,这不起作用。在这种情况下,“拥有”方是模棱两可的。看起来可能根本不支持(不是很清楚)stackoverflow.com/questions/5818373/…
    • 你说得对,我低估了关系不是基于PK的。我认为 Hibernate 足够聪明,可以通过正确的映射来计算它,但事实并非如此。请查看更新。然而,这是我提出的第一个想法,即使它有效,也可能有更好的解决方案,这只是一种解决方法。
    • 谢谢,也许以后有时间看看。现在我将它们保留为没有关系的实体,只处理代码中必要的“加入”。将来我可能只会添加 FK 并拥有标准映射!感谢您抽出宝贵时间调查 Michele。
    【解决方案2】:

    一对一的关系定义如下:

    class UserEntity {
      ....
    
      @Column(length = 20)
      private String userId;
    
      @OneToOne(cascade = CascadeType.ALL)
      @JoinColumn(name = "userId", referencedColumnName = "userId", insertable = false, updatable = false)
      private AddressEntity address;
    
    }
    
    class AddressEntity {
      ....
    
      @Column(length = 20)
      private String userId;
    
      .....
    }
    
    

    【讨论】:

      猜你喜欢
      • 2010-10-14
      • 2013-03-25
      • 2020-03-15
      • 2021-03-15
      • 2021-07-13
      • 1970-01-01
      • 2014-10-12
      • 1970-01-01
      • 2014-12-27
      相关资源
      最近更新 更多