休眠关系(一对多,多对多,一对一)

时间:2020-04-05 16:07:28

标签: hibernate

我正在从事一个项目,并希望在其中实施Hibernate。我已经看过许多讨论关系的视频,并给出了诸如“客户订单”之类的示例,在该示例中,一个客户可以拥有许多订单,但“订单”仅指一个客户。或者,学生-课程,学生可以参加许多课程,反之亦然。在我的特定情况下,我有一个Customer表和Salutation表,我想确保使用了一套标准的称呼语,所以我有以下内容:

enter image description here

我不确定如何设置这种类型的关系。在我所看到的所有示例中,您都将在Customer表中放置对Salutation对象的引用,但这似乎没有任何意义。下面我的客户表中的称呼字段是int类型。那么如何为这样的实例形成关系?谢谢您的帮助!

2 个答案:

答案 0 :(得分:1)

正如您所说的,您正在学习并且没有标记hibernatespring之类的spring-boot以外的其他标记,请让我详细介绍一下您的工作。

让我们以Cart and Items表为例。 。 。 。 。我们将外键约束用于一对多映射:

CREATE TABLE `Cart` (
  `cart_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
 
CREATE TABLE `Items` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `cart_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `cart_id` (`cart_id`),
  CONSTRAINT `items_ibfk_1` FOREIGN KEY (`cart_id`) REFERENCES `Cart` (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

如果您使用的是springboot,则可以跳过配置步骤,因为它仅通过一个依赖项进行了预配置。

Maven依赖项

然后,我们将Hibernate和H2驱动程序依赖项添加到我们的pom.xml文件中。 Hibernate依赖项使用JBoss日志记录,它会自动添加为传递性依赖项:

  • 休眠版本5.2.7.Final
  • H2驱动程序版本1.4.197

请访问Maven中央存储库以获取最新版本的Hibernate和H2依赖项。

休眠配置

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">org.h2.Driver</property>
        <property name="hibernate.connection.password"></property>
        <property name="hibernate.connection.url">
          jdbc:h2:mem:spring_hibernate_one_to_many</property>
        <property name="hibernate.connection.username">sa</property>
        <property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.show_sql">true</property>
    </session-factory>
</hibernate-configuration>

HibernateAnnotationUtil类

private static SessionFactory sessionFactory;
 
private SessionFactory buildSessionFactory() {
      
    ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().
      configure("hibernate-annotation.cfg.xml").build();
    Metadata metadata = new MetadataSources(serviceRegistry).getMetadataBuilder().build();
    SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build();
             
    return sessionFactory;
}   
 
public SessionFactory getSessionFactory() {
    if(sessionFactory == null) sessionFactory = buildSessionFactory();
    return sessionFactory;
}

模型

与映射相关的配置将使用模型类中的JPA注释完成:

@Entity
@Table(name="CART")
public class Cart {
 
    //...
 
    @OneToMany(mappedBy="cart")
    private Set<Items> items;
     
    // getters and setters
}

请注意,@OneToMany批注用于定义Items类中的属性,该属性将用于映射mappingBy变量。这就是为什么我们在Items类中有一个名为“ cart”的属性的原因:

@Entity
@Table(name="ITEMS")
public class Items {
     
    //...
    @ManyToOne
    @JoinColumn(name="cart_id", nullable=false)
    private Cart cart;
 
    public Items() {}
     
    // getters and setters
}

实际操作

在测试程序中,我们正在使用main()方法创建一个类,以获取Hibernate Session并将模型对象保存到数据库中,以实现一对多关联:

sessionFactory = HibernateAnnotationUtil.getSessionFactory();
session = sessionFactory.getCurrentSession();
System.out.println("Session created");
         
tx = session.beginTransaction();
 
session.save(cart);
session.save(item1);
session.save(item2);
         
tx.commit();
System.out.println("Cart ID=" + cart.getId());
System.out.println("item1 ID=" + item1.getId()
  + ", Foreign Key Cart ID=" + item.getCart().getId());
System.out.println("item2 ID=" + item2.getId()
+ ", Foreign Key Cart ID=" + item.getCart().getId());

这是我们测试程序的输出:

 Session created    

Hibernate: insert into CART values ()    

Hibernate: insert into ITEMS (cart_id)

  values (?)    

Hibernate: insert into ITEMS (cart_id)    

  values (?)

Cart ID=7


item1 ID=11, Foreign Key Cart ID=7


item2 ID=12, Foreign Key Cart ID=7


Closing SessionFactory

@ManyToOne批注

如上所述,我们可以使用many-to-one批注指定@ManyToOne关系。 many-to-one映射意味着此实体的许多实例都映射到另一个实体的一个实例–一个购物车中的许多物品。

@ManyToOne 注释也使我们能够创建双向关系。我将进一步详细介绍。

不一致和所有权

现在,如果购物车引用了商品,但商品又没有引用购物车,则我们的关系将是单向的。这些对象也将具有自然的一致性。

但是,在我们的案例中,这种关系是双向的,从而可能导致不一致。

让我们想象一下这样一种情况:开发人员想要将item1添加到购物车中,并将item2添加到cart2,但是犯了一个错误,从而使cart2和item2之间的引用不一致:

Cart cart1 = new Cart();
Cart cart2 = new Cart();
 
Items item1 = new Items(cart1);
Items item2 = new Items(cart2); 
Set<Items> itemsSet = new HashSet<Items>();
itemsSet.add(item1);
itemsSet.add(item2); 
cart1.setItems(itemsSet); // wrong!

如上所示,item2引用了cart2,而cart2没有引用item2,这很糟糕。

Hibernate如何将item2保存到数据库?物品2 foreign key是否引用购物车1或购物车2?

我们使用关系的拥有方的想法解决了这种歧义-属于拥有方的引用具有优先权,并保存到数据库中。

作为拥有方 最好将多对一方标记为拥有方。

换句话说,Item是拥有方,而Cart是相反方,这正是我们之前所做的。

那么,我们如何实现这一目标?

通过在Cart类中包含mappingBy属性,我们将其标记为反面。

同时,我们还使用@ManyToOne注释Items.cart字段,使Items成为拥有方。

回到我们的“不一致”示例,现在,Hibernate知道item2的引用更为重要,并且会将item2的引用保存到数据库中。

让我们检查一下结果:

item1 ID=1, Foreign Key Cart ID=1

item2 ID=2, Foreign Key Cart ID=2

推车为拥有方

也可以将一对多的一面标记为拥有面,将多对一的面标记为反面。

尽管不建议这样做,但让我们继续尝试一下。

下面的代码片段显示了一对多方作为拥有方的实现:

public class ItemsOIO {
     
    //  ...
    @ManyToOne
    @JoinColumn(name = "cart_id", insertable = false, updatable = false)
    private CartOIO cart;
    //..
}
 
public class CartOIO {
     
    //..  
    @OneToMany
    @JoinColumn(name = "cart_id") // we need to duplicate the physical information
    private Set<ItemsOIO> items;
    //..
}

请注意我们如何删除mapedBy元素并将多对一@JoinColumn设置为可插入且可更新为false。

如果我们运行相同的代码,结果将相反:

item1 ID=1, Foreign Key Cart ID=1

item2 ID=2, Foreign Key Cart ID=1

如上所示,现在item2属于购物车。

希望从学习前瞻就足够了。

答案 1 :(得分:0)

我认为您必须了解各种关系。

您使用的图显示在另一个实体中外部化的one-to-one relationship。您可以这样做,但是没有什么可以强迫您这样做。大多数时候情况都很好。自由。

在我的特定情况下,我有一个Customer表和一个Salutation表,我想在其中确保标准的称呼集

您输入的文字告诉我您有1个客户和许多可能的称呼。如果网络用户只能选择其中之一,那么您的图表就很好。

否则,如果网络用户可以选择许多选项,则必须查看one-to-many relationship

Many-to-many relationships也已经存在,但您不在那种用例中。

您可以从首选关系数据库的官方文档中学习。但是要了解的基础知识在SQL教程中。我喜欢这些oneHibernate的人。