(一) 描述
现实项目一般不会只有一个表,而且多表之间还会有关系。
(二) 集合映射
2.1 Set 映射
用户有多个地址,地址不能重复,用 Set 保存。
2.2 List 映射
用户有多个昵称,用 list 保存。
2.3 Map 映射
用户有很多卡片,卡片有标号和描述,通过标号能找到描述。
2.4 设计实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Data public class User {
private int id;
private Date birthday;
private Date createTime;
private String name;
private int status;
private Date updateTime;
private int countryId;
private Set<String> address = new HashSet<>();
private List<String> nickNames = new ArrayList<>();
private Map<Integer,String> card = new HashMap<>(); }
|
2.5 思考
对于 Set 映射,我们需要在数据库中创建一个从表,名字就是 Set 集合的字段名字,叫 address,表中要有一个外键指向 user 表的主键,这样才能建立起联系。
对于 List 映射,我们和 Set 集合应该想法是一样的,不过 List 集合是有顺序的,所以我们增加一个列用来描述顺序。
对于 Map 映射,跟 List 不同的是,map 是键值对存储,所以我们要有一列存储键,一列存储值。
在 mapping 文件中是这样配置的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.xiaobu.learn_hibernate.entity"> <class name="User" lazy="true" table="user"> <id name="id" column="id"> <generator class="native"/> </id>
<property name="birthday" column="birthday"/> <property name="countryId" column="country_id"/> <property name="createTime" column="create_time"/> <property name="name" column="name"/> <property name="status" column="status"/> <property name="updateTime" column="update_time"/>
<set name="address" table="address"> <key column="user_id"/> <element column="address" type="java.lang.String"/> </set>
<list name="nickNames" table="nick_name"> <key column="user_id"/> <index column="nick_index"/> <element column="nick_name" type="java.lang.String"/> </list>
<map name="card" table="card"> <key column="user_id"/> <map-key column="card_id" type="java.lang.Integer"/> <element column="card_description" type="java.lang.String"/> </map> </class> </hibernate-mapping>
|
2.6 测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| public class TestSetListMap extends TestApplication{ @Test public void testSet() { User user = new User(); user.setName("set用户"); user.setStatus(0); user.setCreateTime(new Date()); user.setUpdateTime(new Date()); user.setBirthday(new Date()); user.setCountryId(200); user.getAddress().add("河南"); user.getAddress().add("济宁"); user.getAddress().add("临沂");
Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); Serializable key = session.save(user); log.info("key = " + key); tx.commit(); session.close(); } @Test public void testList(){ User user = new User(); user.setName("list用户"); user.setStatus(0); user.setCreateTime(new Date()); user.setUpdateTime(new Date()); user.setBirthday(new Date()); user.setCountryId(200);
user.getAddress().add("河北"); user.getAddress().add("青岛");
user.getNickNames().add("捣乱黄"); user.getNickNames().add("捣乱红"); user.getNickNames().add("捣乱绿");
Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); Serializable key = session.save(user); log.info("key = " + key); tx.commit(); session.close(); }
@Test public void testMap(){ User user = new User(); user.setName("set用户"); user.setStatus(0); user.setCreateTime(new Date()); user.setUpdateTime(new Date()); user.setBirthday(new Date()); user.setCountryId(200);
user.getAddress().add("云南"); user.getAddress().add("四川");
user.getNickNames().add("狗蛋");
user.getCard().put(1001,"第一张卡片"); user.getCard().put(1002,"第二张卡片");
Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); Serializable key = session.save(user); log.info("key = " + key); tx.commit(); session.close(); } }
|
(三) 对象映射 - 一对多&多对一
3.1 引入
上面中集合只映射了一种类型的值,如果集合里面不只是一个单一的类型,而是一个对象类型,就需要用到对象的映射。
考虑两个类,学生 Student 和 教室 ClassRoom
一个 ClassRoom 中可以有许多学生,多个学生在一个教室中,并且一个学生只能在一个教室上课(垃圾学校)
3.2 实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
@Data public class Student { private ClassRoom classRoom;
private String name;
private Integer no; }
|
1 2 3 4 5 6 7 8 9 10 11 12
|
@Data public class ClassRoom { private Set<Student> students = new HashSet<>();
private Integer classNo; }
|
3.3 思考
学生和教室在数据库中是两个数据表。
学生与教室在数据库中的关系是多对一,我们需要在学生这一方增加一列作为外键指向教室的主键,以此来建立联系。
3.4 映射文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xiaobu.learn_hibernate.entity"> <class name="Student" lazy="true" table="student"> <id column="stu_no" name="no"> <generator class="native"/> </id> <property name="name" column="name" type="java.lang.String"/>
<many-to-one name="classRoom" column="class_room_id" cascade="all"/> </class> </hibernate-mapping>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xiaobu.learn_hibernate.entity"> <class name="ClassRoom" lazy="true" table="class_room"> <id name="classNo" column="class_no"> <generator class="native"/> </id> <set name="students" table="student" cascade="all"> <key column="class_no"/> <one-to-many class="Student"/> </set> </class> </hibernate-mapping>
|
cascade = all,表示对这个对象的所有操作都会级联操作关联对象,这样在保存新对象的时候如果没有关联对象的对象的时候就会自动创建,不然就会报错。
3.5 测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class TestOneToManyAndManyToOne extends TestApplication {
@Test public void testOneToMany() { Student student1 = new Student(); student1.setName("小刘"); Student student2 = new Student(); student2.setName("小王");
ClassRoom classRoom = new ClassRoom(); classRoom.getStudents().add(student1); classRoom.getStudents().add(student2);
Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); session.save(classRoom); tx.commit(); session.close(); }
@Test public void testManyToOne() { ClassRoom classRoom = new ClassRoom();
Student student1 = new Student(); student1.setClassRoom(classRoom); student1.setName("小刘"); Student student2 = new Student(); student2.setClassRoom(classRoom); student2.setName("小王");
Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); session.save(student1); session.save(student2); tx.commit(); session.close(); } }
|
(四) 对象映射 - 多对多
4.1 引入
除了一对多&多对一关系外,还有多对多关系。
考虑一个文章类,一篇文章有多个标签,一个标签可以标注多篇文章。
4.2 实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
@Data public class Article { private Set<Tag> tags = new HashSet<>();
private Integer id;
private String title;
private String content; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
|
@Data public class Tag { private Set<Article> articles = new HashSet<>();
private String name;
private Integer id; }
|
4.3 思考
文章类在数据库中需要一个数据表,标签类在数据库中需要一个数据表,二者之间是多对多的关系,无法在某一方用外键来维护关系,所以我们要借助中间表,中间表的主键是复合主键,复合了文章表和标签表的主键,而两个键又是两个外键,分别指向两个表。这个中间表不是程序的核心表,所以不需要设计实体类。
文章类包含一个标签的集合,标签类包含一个文章的集合,我们要在配置文件中展示的东西有:
4.4 映射文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xiaobu.learn_hibernate.entity"> <class name="Article" lazy="true" table="article"> <id name="id"> <generator class="native"/> </id> <property name="title" type="java.lang.String"/> <property name="content" type="java.lang.String"/>
<set name="tags" table="article_tag" cascade="all"> <key column="article_id"/> <many-to-many column="tag_id" class="Tag"/> </set>
</class> </hibernate-mapping>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xiaobu.learn_hibernate.entity"> <class name="Tag" lazy="true" table="tag"> <id name="id"> <generator class="native"/> </id>
<property name="name"/>
<set name="articles" table="article_tag" cascade="save-update"> <key column="tag_id"/> <many-to-many column="article_id" class="Article"/> </set>
</class> </hibernate-mapping>
|
cascade = “save-update” 表示此数据表保存或更新数据的时候级联操作关联表。
4.5 测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
public class TestManyToMany extends TestApplication { @Test public void test() { Tag tag1 = new Tag(); Tag tag2 = new Tag(); tag1.setName("冒险"); tag2.setName("搞笑");
Article article = new Article(); article.setTitle("鲁滨逊漂流记"); article.setContent("鲁滨逊在漂流"); article.getTags().add(tag1); article.getTags().add(tag2);
Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); Serializable key = session.save(article); log.info("key == " + key); tx.commit(); session.close(); }
@Test public void test_v2() { Article article1 = new Article(); Article article2 = new Article(); article1.setTitle("鲁滨逊漂流记"); article1.setContent("鲁滨逊在漂流"); article2.setTitle("呐喊"); article2.setContent("鲁迅在呐喊");
Tag tag = new Tag(); tag.setName("搞笑"); tag.getArticles().add(article1); tag.getArticles().add(article2);
Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); Serializable key = session.save(tag); log.info("key == " + key); tx.commit(); session.close(); } }
|
(五) 文件映射 - 一对一
5.1 引入
除了上面两个外,还有一对一,使用场景不多,但会用到。
考虑一个消费者类,消费者有一个消费密码,一个消费密码只属于一个消费者,由于密码重要性,将其单独保存。
5.2 实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
@Data public class Customer { private PayPassword payPassword;
private Integer id;
private String name; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
@Data public class PayPassword { private Customer customer;
private Integer id;
private String password; }
|
5.3 思考
消费者在数据库中对应一张表,支付密码在数据库对应一张表,两者之间是一对一关系,可以用两种方式来描述这种关系:
- 在支付密码表中添加一个外键列,指向消费者的主键列。
- 支付密码的主键列指向消费者的主键列,也就是说,支付密码的主键同时是外键。
5.4 映射文件
两种方式的 Customer 映射文件相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xiaobu.learn_hibernate.entity"> <class name="Customer" lazy="true" table="customer"> <id column="id" name="id"> <generator class="native"/> </id> <property name="name" type="java.lang.String"/>
<one-to-one name="payPassword" class="PayPassword" cascade="save-update"/> </class> </hibernate-mapping>
|
第一种方式的 PayPassword 映射文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xiaobu.learn_hibernate.entity"> <class name="PayPassword" lazy="true" table="pay_password"> <id column="id" name="id"> <generator class="native"/> </id>
<property name="password"/>
<many-to-one name="customer" column="customer_id" unique="true" class="Customer" cascade="save-update"/> </class> </hibernate-mapping>
|
第二种方式的 PayPassword 映射文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.xiaobu.learn_hibernate.entity"> <class name="PayPassword" lazy="true" table="pay_password"> <id column="id" name="id"> <generator class="foreign"> <param name="property">customer</param> </generator> </id>
<property name="password"/> <one-to-one name="customer" class="Customer" cascade="save-update"/> </class> </hibernate-mapping>
|
5.5 测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
public class TestOneToOne extends TestApplication{ @Test public void test(){ PayPassword payPassword = new PayPassword(); payPassword.setPassword("123456789");
Customer customer1 = new Customer(); Customer customer2 = new Customer(); customer1.setName("王笑笑"); customer1.setPayPassword(payPassword); customer2.setName("刘飒飒"); customer2.setPayPassword(payPassword);
Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); Serializable key = session.save(customer1); log.info("key == " + key); key = session.save(customer2); log.info("key == " + key); tx.commit(); session.close(); }
@Test public void test_v2(){ Customer customer = new Customer(); customer.setName("张小侯");
PayPassword payPassword = new PayPassword(); payPassword.setPassword("123321"); payPassword.setCustomer(customer);
Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); Serializable key = session.save(payPassword); log.info("key == " + key); tx.commit(); session.close(); } }
|