浅谈Hibernate中映射集合属性以及主键和外键

本文详细介绍Hibernate框架中如何映射各种集合属性,包括List、Array、Set及Map,并演示了具体的Java代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先说明一下什么叫主键以及外键。

主键和外键是对于数据库来说的,数据库的表才有主键外键的说法。

主键:主键是指标识某个表中唯一且非空的一条记录行的列,这个列中的值不允许有重复的值,用于确定一个表中的一条记录,实

    际上主键就是告诉别人:这个数据列是唯一的。

外键:引用另外一个表中的主键,在当前表中不一定为唯一的,但是在被引用表中一般唯一。对于关系型数据库来说(比如MySQL)

    外键是联系数据表之间的唯一方式,主要目的是控制存储在外键表中的数据。

建立外键的前提:本表的列必须与外键类型相同(外键必须是外表主键)。

例如:

表1:用户编号(主键)  用户名称  部门编号(外键)
表2:部门编号(主键)  部门名称  
这里来讲:对于表1来说,用户编号是主键,对于表2来讲部门编号是主键,然后表1引用表2的主键作为外键,表2为主表,表1为从表,这样就将两个数据表关联起来了,表一中每个部门编号表二都会有个部门名称相对应。

下面开始映射集合属性:

因为关于Hibernate中映射List、Array、Set、以及Map的方式相差不大,此处仅拿映射List展开,其他稍作介绍。


Hibernate映射List集合


1:PersonList.java

Person类中除了普通属性外,还有一个List集合属性:schools,该属性对应多个学校。

package com.mao;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.*;

@Entity
@Table(name="personList_inf")
public class PersonList {
	//分别指定主键、主键列名、主键为自增
	@Id
	@Column(name="person_id",nullable=false)
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Integer id;
	@Column (name="person_name",length=50)
	private String name;
	@Lob
	@Basic(fetch=FetchType.LAZY)
	private byte[]pic;
	//通过@Temporal注解 可以完成持久化属性映射到数据库的时间格式
	@Temporal(TemporalType.DATE)
	private Date birth;
	//如果集合通过泛型指定了类型 则可以不通过targetClass指定类型
	@ElementCollection(targetClass=String.class)
	//指定保存集合的数据表 并指定外键列
	@CollectionTable(name="schools_inf",joinColumns=@JoinColumn (name="person_id",nullable=false))
	//指定保存集合元素的列为:school_name
	@Column (name="school_name")
	//映射集合元素的索引的列
	@OrderColumn(name="list_order")
	private List<String> school=new ArrayList<>();
	//省略相应的set、get方法
	
		
}

2:主程序 PersonManagerList.java

package com.mao;

import java.io.File;
import java.io.FileInputStream;
import java.util.Date;

import org.hibernate.*;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class PersonManagerList
{
	public static void main(String[]args) throws Exception{
		Configuration cof=new Configuration().configure();
		ServiceRegistryBuilder serviceRegistryBuilder=new ServiceRegistryBuilder().applySettings(cof.getProperties());
		ServiceRegistry registry=serviceRegistryBuilder.buildServiceRegistry();
		SessionFactory sf=cof.buildSessionFactory(registry);
		Session sess=sf.openSession();
		Transaction tx=sess.beginTransaction();
	        PersonList person=new PersonList();
		person.setName("VipMao");
		//创建文件 上传图片到数据库
		File file=new File("image/2.jpg");
		byte[]content=new byte[(int) file.length()];
		//创建读取数据流来读取图片对象file并存入字节数组content
		new FileInputStream(file).read(content);
		person.setPic(content);
		person.setBirth(new Date());
		person.getSchool().add("菜鸟学院");
		//保存信息并将对象持久化
		sess.save(person);
		//提交事务
		tx.commit();
		sess.close();
		sf.close();
	}
}


3:配置文件 hibernate.cfg.xml(后面配置文件不在列出)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.connection.characterEncoding">utf-8</property>
         <!-- 指定连接池最大连接数 -->
        <property name="hibernate.c3p0.max_size">20</property>
        <!-- 指定连接池最小连接数 -->
        <property name="hibernate.c3p0.min_size">1</property>
        <!-- 指定连接池里连接超时时长 -->
        <property name="hibernate.c3p0.timeout">5000</property>
        <!-- 指定连接池里做大缓存多少个Statement对象 -->
        <property name="hibernate.c3p0.max_statements">50</property>
        <!-- 是否根据需要自动建表 -->
        <property name="hbm2ddl.auto">update</property>
        <!-- 是否显示sql语句 -->
        <property name="show_sql">true</property>
        <!-- 将SQL脚本进行格式化后再输出 -->
        <property name="hibernate.format_sql">true</property>
        <!-- 设置连接数据库所使用的方言 -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <!-- 罗列所有持久化类名 -->
      <!--   
        <mapping class="com.mao.Person"/>
        <mapping class="com.mao.PersonMap"/>
        <mapping class="com.mao.PersonArray"/>
        <mapping class="com.mao.PersonSet"/>
         -->
        <mapping class="com.mao.PersonList"/>
    </session-factory>
</hibernate-configuration><span style="font-size:14px;">
</span>

4:运行结果


从控制台输出的SQL语句可以看出,先在personList_inf表中插入了普通属性,然后通过外键列在schools_inf表中插入集合信息,下面数据库结果:



这样person_id是表一的主键 ,然后表二把表一的主键作为外键,这样两个表就通过person_id联系起来了。

5:映射集合常用的注解

 @ElementCollection:用于映射集合属性

  @CollectionTable:用于映射集合属性表,name属性指定存储集合属性表的表名,joinColumns属性映射外键列

  @Column:用于映射保存集合元素的数据列

  @OrderColumn:用于映射集合的索引列,像Set这种无序集合可以不用此注解


Hibernate映射Array集合


1:PersonArray.java(只给出集合部分变动)

@ElementCollection(targetClass=String.class)
	@CollectionTable(name="arraySchool_inf" ,joinColumns=@JoinColumn(name="id",nullable=false))
	//指定保存集合元素的列为school_name
	@Column(name="school_name")
	//映射集合元素的索引
	@OrderColumn(name="array_order")
	private String[]school;

2:PersonManagerArray.java

 PersonArray person=new PersonArray();
		person.setName("VipMao");
		File file=new File("image/2.jpg");
		byte[]content=new byte[(int) file.length()];
		//创建读取数据流来读取图片对象file并存入字节数组content
		new FileInputStream(file).read(content);
		person.setPic(content);
		person.setBirth(new Date());
		System.out.println("日期为:"+new Date());
		String[]school=new String[3];
		school[0]="菜鸟学院";
		school[1]="小牛学院";
		school[2]="大牛学院";
		person.setSchool(school);
		sess.save(person);

3:运行结果




Hibernate映射Set集合

 

1:PersonSet.java

@ElementCollection
	@CollectionTable(name="schoolSet_inf" , joinColumns=@JoinColumn(name="person_id",nullable=false))
	@Column(name="school_name",nullable=false)
	private Set<String>schools=new HashSet<>();

2:PersonManagerSet.java

PersonSet person=new PersonSet();
		person.setName("VipMao");
		person.setAge("24");
		person.getSchools().add("菜鸟学院");
		person.getSchools().add("小牛学院");
		person.getSchools().add("大牛学院");
		sess.save(person);

3:运行结果



可以看出,因为Set集合的无序性,我们并没有通过@OrderColumn映射集合的索引列。


Hibernate映射Map集合


1:PersonMap.java

@ElementCollection(targetClass=Float.class)
	@CollectionTable(name="score_inf",joinColumns=@JoinColumn(name="person_id",nullable=false))
	//映射保存Map key的数据列
	@MapKeyColumn(name="subject_name")
	//指定Map key的类型为String类型
	@MapKeyClass(String.class)
	//映射保存 Map value的数据列
	@Column(name="mark")
	private Map<String,Float>scores=new HashMap<>();

2:PersonManagerMap.java

PersonMap person = new PersonMap();
		person.setAge("24");
		person.setName("VipMao");
		// 向person的Map集合属性中添加key-value对
		person.getScores().put("语文" , 90f);
		person.getScores().put("英文" , 99f);
		session.save(person);

3:运行结果



最后说一下各集合的联合主键问题:

List联合主键:持久化外键列+集合元素索引列

Set联合主键:持久化外键列+Set元素列(元素列不能为空)

Map联合主键:持久化外键列+集合Key列


<think>好的,用户之前让我介绍了数据库的类型,现在他们想了解主键。我需要先回顾一下之前的内容,确保这次回答能衔接上。主键在关系型数据库中非常重要,用户可能是在学习数据库设计或者准备考试,需要明确这两个概念的区别用途。 首先,主键的定义特点。主键是唯一标识表中每条记录的字段,必须唯一且非空。我需要举一些例子,比如用学号作为主键,或者自增ID。还要说明主键可以有单字段或者复合主键,比如订单号加产品号的组合。 然后用于建立表之间的关系,它指向另一个表的主键。需要强调的作用是保持数据的一致性完整性,比如在订单表中,用户ID作为指向用户表的主键,确保订单属于存在的用户。还要提到约束的行为,比如级联更新或删除,以及可能出现的性能问题。 用户可能的深层需求是理解如何设计数据库表结构,或者解决数据完整性问题。要确保解释清楚主键在实际中的应用场景,比如避免重复数据,维护表之间的关系。可能需要提到不使用的情况,比如在高并发场景下,可能用应用层逻辑代替约束,但这样需要自己处理一致性。 还要注意使用之前的格式,分点说明,保持结构清晰,代码示例用正确的SQL语法。比如创建表时定义主键的语句,以及插入数据时的错误情况。这样用户能直观看到语法应用方式。 最后,总结主键的重要性,以及在不同场景下的使用建议。确保回答真实可靠,结构清晰,帮助用户逐步理解这两个概念。</think>### 主键详解 主键(**Primary Key**)(**Foreign Key**)是关系型数据库中用于维护数据完整性表间关联的核心机制。以下是两者的定义、作用及实际应用场景: --- #### 一、主键(Primary Key) ##### **1. 定义** - 主键是表中**唯一标识每一行数据**的字段或字段组合,确保每条记录的唯一性。 - **核心特性**: - **唯一性**:主键值不可重复。 - **非空性**:主键字段不能为 `NULL`。 - **稳定性**:主键值通常不随业务逻辑变化而修改。 ##### **2. 作用** - **唯一标识记录**:例如,用户表中的 `user_id` 唯一确定一个用户。 - **加速数据检索**:主键默认生成唯一索引(如 `PRIMARY INDEX`),提升查询效率。 - **支持表间关联**:作为的引用目标,建立表与表的关系。 ##### **3. 设计规则** - **单字段主键**:常见于自增整数(如 `id INT AUTO_INCREMENT`)或业务唯一标识(如身份证号)。 - **复合主键**:多个字段联合唯一(如订单表中的 `order_id` + `product_id`)。 - **示例**: ```sql CREATE TABLE users ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE ); ``` --- #### 二、(Foreign Key) ##### **1. 定义** - 是表中指向另一个表主键的字段,用于**建立表与表之间的关联关系**。 - **核心特性**: - **引用完整性**:值必须在被引用表的主键中存在(除非允许 `NULL`)。 - **级联操作**:可定义删除或更新时的联动行为(如 `CASCADE`、`SET NULL`)。 ##### **2. 作用** - **维护数据一致性**:例如,订单表中的 `user_id` 必须指向有效的用户,避免“孤儿订单”。 - **实现数据关联查询**:通过 `JOIN` 操作跨表获取关联数据(如查询用户的所有订单)。 ##### **3. 设计规则** - **约束**:需显式定义关联的主键字段。 - **示例**: ```sql CREATE TABLE orders ( order_id INT PRIMARY KEY, user_id INT, amount DECIMAL(10,2), FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE -- 用户删除时,其订单自动删除 ); ``` --- #### 三、主键的对比 | **特性** | **主键** | **** | |------------------|-----------------------------------|-----------------------------------| | **唯一性** | 必须唯一 | 可重复(除非联合唯一约束) | | **空值** | 禁止 `NULL` | 允许 `NULL`(根据业务需求) | | **作用范围** | 单表内标识记录 | 跨表建立关联关系 | | **索引** | 自动创建唯一索引 | 可手动创建索引(非必须) | --- #### 四、实际应用场景 ##### **1. 主键场景** - **用户表**:用 `user_id` 唯一标识用户。 - **商品表**:用 `product_code` 确保商品唯一性。 - **日志表**:使用自增主键 `log_id` 快速插入检索。 ##### **2. 场景** - **订单与用户**:订单表中的 `user_id` 关联用户表。 - **评论与文章**:评论表的 `article_id` 指向文章表的主键。 - **部门与员工**:员工表的 `department_id` 关联部门表。 --- #### 五、注意事项 1. **性能**: - 约束可能影响写入性能(需检查引用完整性),高并发场景可权衡是否使用。 - 可通过应用层逻辑替代(但需自行保证数据一致性)。 2. **主键选择**: - 避免使用业务含义字段(如身份证号)作为主键,优先选择代理(如自增 `id`)。 - 复合主键需谨慎设计,可能增加索引复杂度。 3. **级联操作风险**: - `ON DELETE CASCADE` 可能导致数据意删除,需结合业务需求配置。 --- #### 六、总结 - **主键**是数据的“身份证”,****是表间的“桥梁”。 - 合理使用主键,可有效保障数据一致性关联查询效率。 - 在分布式或高吞吐场景中,可能需弱化约束,改用其他机制(如最终一致性)。 如需具体案例分析或 SQL 代码调试,可提供更多细节!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值