Hibernate多对多映射项目搭建

本文详细介绍了如何在Hibernate中实现多对多映射,包括实现思路、数据库设计及操作、实体类与映射配置、DAO方法实现、JUnit测试以及注意事项。重点讲解了中间表的处理和主控方的选择,帮助理解多对多映射的原理。
摘要由CSDN通过智能技术生成

前言

hibernate中既有一对多映射关系,也有直接映射的多对多关联关系,多对多的映射关系可以看作是两个一对多的映射!

多对多映射

以下的案例可以看成是一本书对应多个类别,一个类别对应多本书的之间的映射关系!

web.xml:

将原有的web版本修改为3.0的版本就好!

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="WebApp_ID" version="3.0">
</web-app>

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.wangqiuping</groupId>
  <artifactId>hibernateManytoMany</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>hibernateManytoMany Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
      <!--junit依赖 -->
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>3.8.1</version>
	<scope>test</scope>
</dependency>
<!--servlet依赖 -->
<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>javax.servlet-api</artifactId>
	<version>4.0.1</version>
	<scope>provided</scope>
</dependency>
<!--mysql依赖 -->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.6</version>
	</dependency>
<!--Tomcat的jsp依赖  -->
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jsp-api -->
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-jsp-api</artifactId>
    <version>8.5.0</version>
</dependency>
	
<!--hibernate依赖  -->
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.2.12.Final</version>
</dependency>
</dependencies>
<build>
<finalName>hibernateManytoMany</finalName>
<!--jdk依赖 -->
<plugins>
	<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-compiler-plugin</artifactId>
	<version>3.7.0</version>
	<configuration>
		<source>1.8</source>
		<target>1.8</target>
		<encoding>UTF-8</encoding>
	</configuration>
	</plugin>
</plugins>
</build>
</project>

1、实现思路

多对多:一张表包含另外一张表的set对象,中间表不需要新建实体类

2、使用到的数据库

数据库脚本

-- 书本类别表
create table t_category_hb
(
   category_id int primary key auto_increment,
   category_name varchar(50) not null
);

-- 书本表
create table t_book_hb
(
   book_id int primary key auto_increment,
   book_name varchar(50) not null,
   price float not null
);


-- 桥接表
-- 定义三个列,其实只要两个列
-- 一个类别对应多本书,一本书对应多个类别
CREATE TABLE t_book_category_hb
(
  bcid INT PRIMARY KEY AUTO_INCREMENT,  
  bid INT NOT NULL,
  cid INT NOT NULL,
  FOREIGN KEY(bid) REFERENCES t_book_hb(book_id),
  FOREIGN KEY(cid) REFERENCES t_category_hb(category_id)
);

给数据库表格添加数据:

---给书籍表增加数据
insert into t_book_hb(book_id, book_name, price) values(1,'西游记',50);
insert into t_book_hb(book_id, book_name, price) values(2,'红楼梦',50);
insert into t_book_hb(book_id, book_name, price) values(3,'水浒',50);
insert into t_book_hb(book_id, book_name, price) values(4,'三国演义',50);

---给数据类别表增加数据
insert into t_category_hb(category_id, category_name) values(1,'古典');
insert into t_category_hb(category_id, category_name) values(2,'神话');
insert into t_category_hb(category_id, category_name) values(3,'历史');

---给中间表增加数据
insert into t_book_category_hb(bid, cid) values(1,1);
insert into t_book_category_hb(bid, cid) values(1,2);
insert into t_book_category_hb(bid, cid) values(2,1);
insert into t_book_category_hb(bid, cid) values(3,1);
insert into t_book_category_hb(bid, cid) values(3,3);
insert into t_book_category_hb(bid, cid) values(4,1);
insert into t_book_category_hb(bid, cid) values(4,3);

查询表格:

select * from t_book_hb;
select * from t_category_hb;
select * from t_book_category_hb;

3、执行后的数据库

书籍表
在这里插入图片描述
书籍类别表
在这里插入图片描述

书籍、书籍类别中间表
在这里插入图片描述

代码实现

1、实体类

Book类

放属性的set、get方法、有参、无参构造方法、toString方法

package com.wangqiuping.entity;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

/**
 *  书籍表的实体类
 *  多种书籍对应多种书籍类型
 *  多对多:一张表包含另外一张表的set对象
 *  @author wangqiuping
 *  2020年7月27日  下午6:25:38
 */
public class Book implements Serializable{
  
	private  Integer   bookId;
	private  String    bookName;
	private  float     price;
	Set<Category> categories=new HashSet<Category>();
	
	public Set<Category> getCategories() {
		return categories;
	}
	public void setCategories(Set<Category> categories) {
		this.categories = categories;
	}
	public Integer getBookId() {
		return bookId;
	}
	public void setBookId(Integer bookId) {
		this.bookId = bookId;
	}
	public String getBookName() {
		return bookName;
	}
	public void setBookName(String bookName) {
		this.bookName = bookName;
	}
	public float getPrice() {
		return price;
	}
	public void setPrice(float price) {
		this.price = price;
	}
	public Book() {
		super();
	}
	public Book(Integer bookId, String bookName, float price, Set<Category> categories) {
		super();
		this.bookId = bookId;
		this.bookName = bookName;
		this.price = price;
		this.categories = categories;
	}
	@Override
	public String toString() {
		return "Book [bookId=" + bookId + ", bookName=" + bookName + ", price=" + price + ", categories=" + categories
				+ "]";
	}
}

Category:

package com.wangqiuping.entity;

import java.io.Serializable;
import java.util.HashSet;
/**
 *  书籍类别表的实体类 
 *  一个书籍类型包含多本书
 *  @author wangqiuping
 *  2020年7月27日  下午6:30:48
 */
import java.util.Set;
public class Category implements Serializable{

	private  Integer  categoryId;
	private  String   categoryName;
	Set<Book> books=new HashSet<Book>();

	public Set<Book> getBooks() {
		return books;
	}
	public void setBooks(Set<Book> books) {
		this.books = books;
	}
	public Integer getCategoryId() {
		return categoryId;
	}
	public void setCategoryId(Integer categoryId) {
		this.categoryId = categoryId;
	}
	public String getCategoryName() {
		return categoryName;
	}
	public void setCategoryName(String categoryName) {
		this.categoryName = categoryName;
	}
	
	public Category() {
		super();
	}
	public Category(Integer categoryId, String categoryName, Set<Book> books) {
		super();
		this.categoryId = categoryId;
		this.categoryName = categoryName;
		this.books = books;
	}
	@Override
	public String toString() {
		return "Category [categoryId=" + categoryId + ", categoryName=" + categoryName + ", books=" + books + "]";
	}
}

实体类映射配置文件

hibernate中只能有一个核hibernate.cfg.xml,但是可以有多个实体类映射文件!

hibernate.cfg.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
   <session-factory>
   <!--数据库相关配置 -->
   <!--连接账户名称 -->
   <property name="connection.username">root</property>
   <!--连接账户密码-->
   <property name="connection.password">123</property>
   <!--连接的绝对路径  -->
  <property name="connection.url">jdbc:mysql://localhost:3306/t243?&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;userSSL=false
  </property>
  <!-- 驱动的绝对路径 -->
  <property name="connection.driver_class">
  com.mysql.jdbc.Driver
  </property>
  
   <!--数据库方言的相关配置  -->
   <property name="dialect">org.hibernate.dialect.MySQLDialect
   </property>
   
   <!--调试相关配置 -->
   <!-- hibernate运行过程是否站后四自动生成的SQL代码 -->
   <property name="show_sql">true</property>
   <!-- 是否规范化输出sql代码 -->
   <property name="format_sql">true</property>
   
   <!--实体映射相关配置 -->
   <mapping resource="com/wangqiuping/entity/Book.hbm.xml"/>
   <mapping resource="com/wangqiuping/entity/Category.hbm.xml"/>
   </session-factory>
</hibernate-configuration>

实体类的相关配置一定要加进去,否则配置文件无法读取到实体类的配置文件!

Book.hbm.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
   <class name="com.wangqiuping.entity.Book" table="t_book_hb">
     <id name="bookId" type="java.lang.Integer" column="book_id">
      <generator class="increment"></generator>
    </id>
     <property   name="bookName"   type="java.lang.String"   column="book_name"></property>
     <property   name="price"      type="java.lang.Float"    column="price"></property>
    <!-- 多对多映射关系 -->
    <!--
         name:一方包含多方的属性对象名称 指向多方
         cascade:级联操作   save-update/none/all=delete+save-update
         inverse:是否是主控方  false表示对方不是主控方
                 true表示对方是主控方 由对方来维护中间表
         table:表示中间表的名称
       -->
    <set name="categories"
         cascade="save-update"
         inverse="false"
         table="t_book_category_hb"
         >
         <!--column:指向己方在中间表的外键字段 -->
         <key column="bid"></key>
            <!-- class:对方实例的完整路径
                 column:对方在中间表中的外键字段
             -->
          <many-to-many 
             class="com.wangqiuping.entity.Category"
             column="cid">
          </many-to-many>
    </set>
   </class>
</hibernate-mapping>

Category.hbm.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
   <class name="com.wangqiuping.entity.Category" table="t_category_hb">
     <id name="categoryId" type="java.lang.Integer" column="category_id">
      <generator class="increment"></generator>
    </id>
     <property   name="categoryName"   type="java.lang.String"   column="category_name"></property>
     <set name="books"
         cascade="save-update"
         inverse="true"
         table="t_book_category_hb">
         <key column="cid"></key>
          <many-to-many 
             class="com.wangqiuping.entity.Book"
             column="bid">
          </many-to-many>
    </set>
   </class>
</hibernate-mapping>

2、dao方法

BookDao

package com.wangqiuping.dao;

import org.hibernate.Session;
import org.hibernate.Transaction;
import com.wangqiuping.entity.Book;
import com.wangqiuping.util.SessionFactoryUtils;
/**
 *  书籍的dao方法
 *  @author wangqiuping
 *  2020年7月27日  下午8:25:31
 */
public class BookDao {
  public  void  addBook(Book book) {
	  Session session = SessionFactoryUtils.openSession();
	  Transaction ts = session.beginTransaction();
      session.save(book);
	  ts.commit();
      SessionFactoryUtils.closeSession();
  }	
}

SessionFactoryUtils工具类

package com.wangqiuping.util;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
 * 提供session的开启和关闭
 * @author wangqiuping
 *  2020年7月28日  下午4:01:33
 */
public class SessionFactoryUtils {

	private static final String 
			HIBERNATE_CONFIG_FILE="hibernate.cfg.xml";
	
	private static ThreadLocal<Session> threadLocal=
			new ThreadLocal<Session>();
	
	//创建数据库的会话工厂
	private static SessionFactory sessionFactory;
	
	//读取hibernate核心配置
	private static Configuration configuration;
	
	static {
		try {
			configuration=new Configuration();
			configuration.configure(HIBERNATE_CONFIG_FILE);
			//创建Session会话工厂
			sessionFactory=configuration.buildSessionFactory();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static Session openSession() {
		Session session = threadLocal.get();
		if(null==session) {
			session=sessionFactory.openSession();
			threadLocal.set(session);
		}
		return session;
	}
	
	public static void closeSession() {
		Session session = threadLocal.get();
		if(null!=session) {
			if(session.isOpen())
				session.close();
			threadLocal.set(null);
		}
	}
	
	public static void main(String[] args) {
		Session session = SessionFactoryUtils.openSession();
		System.out.println("Session状态:"+session.isOpen());
		System.out.println("Session会话已打开");
		SessionFactoryUtils.closeSession();
		System.out.println("Session会话已关闭");
	}
}

3、junit测试代码

package com.wangqiuping.dao;

import com.wangqiuping.entity.Book;
import com.wangqiuping.entity.Category;

import junit.framework.TestCase;
/**
 * 
 * @author wangqiuping
 *  2020年7月28日  下午5:32:08
 */
public class BookDaoTest extends TestCase {
    
	BookDao bookDao=new BookDao();
	Book book=null;
	
	
	protected void setUp() throws Exception {
		book=new Book();
		super.setUp();
	}

	public void testAddBook() {
		book.setBookName("皇帝的新衣");
		book.setPrice(100f);
		
		Category  category=new Category();
		category.setCategoryName("寓言");
		
		Category  category1=new Category();
		category1.setCategoryName("故事");
		
	    book.getCategories().add(category);
	    book.getCategories().add(category1);
		
	    category.getBooks().add(book);
	    category1.getBooks().add(book);
	    
	    bookDao.addBook(book);   
	}
}

两次一样的查询、添加数据的效果都是一样的,如图:
在这里插入图片描述

书籍表
在这里插入图片描述

书籍类别表
在这里插入图片描述

书籍类别中间表
在这里插入图片描述

书籍查询

原有的书籍表会增加一条数据,书籍类别表未发生改变,书籍、书籍类别中间表会新增一条数据!

CategoryDao

package com.wangqiuping.dao;

import org.hibernate.Session;
import org.hibernate.Transaction;
import com.wangqiuping.entity.Category;
import com.wangqiuping.util.SessionFactoryUtils;

/**
 * 
 *  @author wangqiuping
 *  2020年7月28日  下午5:58:53
 */
public class CategoryDao {

  public  Category  getCategoryById(Category category) {
	  Session session = SessionFactoryUtils.openSession();
	  Transaction ts = session.beginTransaction();
	  //根据对象和id获取
	  Category c = session.get(Category.class,category.getCategoryId());
	  
	  ts.commit();
      SessionFactoryUtils.closeSession();
      return  c;//返回查询后的结果集
  }
}

junit测试代码

package com.wangqiuping.dao;

import com.wangqiuping.entity.Book;
import com.wangqiuping.entity.Category;
import junit.framework.TestCase;

public class CategoryDaoTest extends TestCase {
     
	CategoryDao  categoryDao=null;
	BookDao      bookDao=new BookDao();
	
	
	protected void setUp() throws Exception {
	   categoryDao=new CategoryDao();
	}

	public void testGetCategoryById() {
	   Book  book=new Book();
	   book.setBookName("解忧杂货店");
	   book.setPrice(100f);
	   
	   Category   category=new Category();
	   category.setCategoryId(4);
	   Category c = categoryDao.getCategoryById(category);
	   
	   book.getCategories().add(c);
	   bookDao.addBook(book);
	
	}
}

在这里插入图片描述
在这里插入图片描述数据库的展示效果:

书籍表
在这里插入图片描述
书籍类别表:

仍然未发生改变!
在这里插入图片描述
书籍、书籍类别中间表:
在这里插入图片描述

数据库的多对多

1、 数据库中不能直接映射多对多

处理:创建一个中间表,将一个多对多关系转换成两个一对多

在这里插入图片描述
数据库多表联接查询永远就是两个表的联接查询
在这里插入图片描述
当然也可以进行交叉连接,以及左联或者右联!

注意事项

1、 一定要定义一个主控方

因为主控方会影响中间表产生的行数,两张表中间只能有一个主控方,两张表都做主控方的话,中间表的影响行数会增加一倍!

2、多对多删除,主控方直接删除,被控方先通过主控方解除多对多关系,再删除被控方

3、 禁用级联删除

4、关联关系编辑,不需要直接操作桥接表,hibernate的主控方会自动维护

总结

hibernate的多对多映射其实就是多个一对多映射,弄清楚主从表之间的关系,谁为主控方,以及映射之间的关联,基本上大致的流程就出来了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值