文章目录
1 一对多 案例解析
1.1 一对多,XML版
映射类的设计:
①新增:
发现一个问题,多了一些update语句
通常, 为避免生成冗余SQL方言, 建议把关联关系的维护主动权交给N的一方,这时需要在1方的set元素 inverse=“false”:表示主外键关系由自己维护;
解决的办法:
②删除
如果在数据库表中没有设置级联,在删除的时候会报错
解决的办法:
1.在数据库表的外键上设置级联
2.在主键表对应的映射文件的set标签中设置cascade=”delete”
cascade属性有如下取值:
none: 不级联
save-update: 级联保存或更新
merge: 级联保存或更新
delete: 级联删除
all: 包含所有级联行为
③修改
id不能修改,关联id的时候,通过id查询对象,然后设计对象即可
④查询
一般查询外键对象信息的同时,查询出关联的主键对象信息
一般查询主键对象信息的同时,不查询出关联的外键对象信息,延迟加载
Hibernate 监听器与拦截器(介绍)
拦截器(Intercept):与Struts2的拦截器机制基本一样,都是一个操作穿过一层层拦截器,每穿过一个拦截器就会触发相应拦截器的事件做预处理或善后处理。
监听器(Listener):其实功能与拦截器类似,但它实现原理不同,它是为每一个事件注册一个或多个监听器,一旦事件发生,则事件源通知所有监听该事件的监听器,然后监听器处理通知(观察者模式)。
以上是老师给的教程,自己写了一个demo,是关于省市县,级联查询的,巩固复习一下,增删改查很简单,就是有下面几个点注意一下:
主表(Provinces):
- 实体类中添加Set< Cities >citySet;
- XML:配置主键表,name属性(Provinces.hbm.xml)
<set name="citiesSet">
<key column="provinceid"></key>
<one-to-many class="com.cn.entity.Cities"/>
</set>
从表(Cities):
- 实体类中把关联的键private String provinceid改成private Provinces provinces;
- XML:一对多,外键表(Cities.hbm.xml)
<many-to-one name="provinces" class="com.cn.entity.Provinces" column="provinceid">
</many-to-one>
hibernate.cfg.xml:
要映射几个表的xml文件
<mapping resource="mapping/Areas.hbm.xml"/>
<mapping resource="mapping/Cities.hbm.xml"/>
<mapping resource="mapping/Provinces.hbm.xml"/>
1.2 一对多,注解版
- 主表(Provinces)
实体类中添加private Set < Cities > citiesSet;
private int id;
private String provinceid;
private String province;
private Set<Cities> citiesSet;
@OneToMany(mappedBy ="provinces",cascade = {CascadeType.ALL})
// @JoinColumn(name = "provinceid")
public Set<Cities> getCitiesSet() {
return citiesSet;
}
//getter,setter...
@Onetomany 的参数:
mappedBy:用于双向关联(两个实体类都写了@manytoone或者@onetomany)时使用,否则会引起数据不一致的问题。
(该属性的值是“多”方class里的“一”方的表名)
fetch:可取的值有FetchType.EAGER和FetchType.LAZY,前者表示主类被加载时加载,后者表示被访问时才会加载
cascade:CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)、CascadeType.REFRESH(级联刷新)、CascadeType.MERGE(级联更新)、CascadeType.ALL(选择全部)
@JoinColumn
与@Column用法相同,区别是@JoinColumn作用的属性必须是实体类
private int id;
private String provinceid;
private String province;
private Set<Cities> citiesSet;
@OneToMany(mappedBy ="provinces",cascade = {CascadeType.ALL})
// @JoinColumn(name = "provinceid")
public Set<Cities> getCitiesSet() {
return citiesSet;
}
@Id
@Column(name = "id")
public int getId() {
return id;
}
// getter/setter...
- 从表(cities)
实体类中把关联的键private String provinceid改成private Provinces provinces;
@ManyToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "provinceid") //cities类中对应外键的属性:provinceid
public Provinces getProvinces() {
return provinces;
}
- hibernate.cfg.xml
检查用户名密码,需要映射实体类
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<mapping class="com.cn.entity.Cities"/>
<mapping class="com.cn.entity.Provinces"/>
在开发时用到@OneToMany注解方式遇到错误:
1、要么在属性上添加注解要么在getter方法上添加注解,不能混合使用;
2、遇到异常:Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn错误,主要原因是,mappedBy="parent"和@JoinColumn(name=“parent_id”)不能同时使用
后来发现在3.5.3版本中@JoinColumn与mappingBy是互斥的,之前在hibernate.3.3.2中都是正确无误的,也就是hibernate.3.3.2允许这两个互相存在。
所以,如果升级到hibernate3.5.3想测试成功的话,mappBy=“parent”,就应该去掉,正确的配置应该是这样:
@OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL)
@JoinColumn(name=“parent_id”)
2 多对多 案例解析
多对多映射类设计:
2.1 多对多,XML版本
- 下载依赖包(pom.xml)
hibernate-core,mysql-connector-java - 创建多对多的实体类(Factory.java,Product.java)
public class Factory {
private Integer facId;
private String facName;
private String facAddres;
private Set<Product> productSet = new HashSet<>(); //防止空指针
public Factory() {}
public Factory(String facName, String facAddres) {
this.facName = facName;
this.facAddres = facAddres;
}
//getter,setter...
}
public class Product {
private Integer proId;
private String proName;
private Double proPrice;
private Set<Factory> factorySet = new HashSet<>();//防止空指针
public Product() {}
public Product(String proName, Double proPrice) {
this.proName = proName;
this.proPrice = proPrice;
}
//getter,setter...
}
- hibernate配置文件(hibernate.cfg.xml)
<session-factory>
<property name="connection.url">jdbc:mysql://localhost:3306/crm</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.username">root</property>
<property name="connection.password">1234</property>
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- DB schema will be updated if needed -->
<!-- update Position.hbm.xml 与数据库进行对比
如果没有表就创建表 如果实体有某个字段 数据库里面没有 就新增一个字段
create 每次启动就新建
-->
<property name="hbm2ddl.auto">update</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<mapping resource="mapping/Factory.hbm.xml"/>
<mapping resource="mapping/Product.hbm.xml"/>
</session-factory>
- 实体类的配置文件
数据库里面没有表,就自己创建表,关联表也是自动创建出来的:
Factory.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.cn.orm.Factory" table="Factory" schema="crm">
<id name="facId">
<column name="facId" sql-type="int(11)"/>
<generator class="native"></generator>
</id>
<property name="facName">
<column name="facName" sql-type="varchar(64)" length="64"/>
</property>
<property name="facAddres">
<column name="facAddres" sql-type="varchar(255)"/>
</property>
<!--name:当前属性 table:第三张表,关联关系表 -->
<set name="productSet" table="product_factory_relation">
<!-- this 当前 字段名 -->
<key column="factoryId"></key>
<!-- class 当前属性的类型 列:对方外键列 -->
<many-to-many class="com.cn.orm.Product" column="productId">
</many-to-many>
</set>
</class>
</hibernate-mapping>
Product.hbm.xml
<hibernate-mapping>
<class name="com.cn.orm.Product" table="Product" schema="crm">
<id name="proId">
<column name="proId" sql-type="int(11)"/>
<generator class="native"></generator>
</id>
<property name="proName">
<column name="proName" sql-type="varchar(64)" length="64"/>
</property>
<property name="proPrice">
<column name="proPrice" sql-type="double"/>
</property>
<set name="factorySet" table="product_factory_relation" >
<key column="productId"></key>
<!-- class:当前属性的类型 column:属性关联的外键字段 -->
<many-to-many class="com.cn.orm.Factory" column="factoryId">
</many-to-many>
</set>
</class>
</hibernate-mapping>
- 测试类
SessionFactoryUtil.java
public class SessionFactoryUtil {//饿√ 懒
private static SessionFactory sessionFactory;
static{
//先解析配置文件
//要写路径 1.没有使用默认名称 2.不在根目录下
Configuration configuration = new Configuration();
//用配置问价对象来获得sessionFactory
sessionFactory = configuration.configure().buildSessionFactory();
}
public static SessionFactory getSessionFactory(){
return sessionFactory;
}
}
测试类AppTest.java
public class AppTest {
Session session;
//测试方法
/* @BeforeClass
public static void beforeClass(){}*/
@Before
public void before(){
session = SessionFactoryUtil.getSessionFactory().openSession();
}
@After
public void after(){
session.close();
}
@Test
public void test(){
System.out.println("---测试建表---");
}
@Test
public void add() {
// 创建工厂对象
Factory factory1 = new Factory("四川皮革厂","金华");
Factory factory2 = new Factory("温江皮革厂","温江");
// 商品
Product product1 = new Product("鳄鱼皮",25.0);
Product product2 = new Product("牛皮",120.0);
Product product3 = new Product("皮衣",280.0);
Product product4 = new Product("牛牛",2220.0);
// 搭建关系 只需要搭建一方的关系
// factory1.setProductSet(new HashSet<>());
factory1.getProductSet().add(product1);
factory1.getProductSet().add(product2);
factory2.getProductSet().add(product3);
factory2.getProductSet().add(product4);
session.save(factory1);
session.save(factory2);
session.save(product1);
session.save(product2);
session.save(product3);
session.save(product4);
session.beginTransaction().commit();//不写这一句的话就不会添加关联表的数据
}
@Test
public void update() {
System.out.println();
}
@Test
public void delete() {
System.out.println();
// 写出解决方案,在 表示需要 程序维护的时候;关系表不需要业务直接维护,所有 默认 级联 第三张表
Product product = session.get(Product.class,1);
// System.out.println("----确定是否延迟加载----");
// System.out.println(product.getFactorySet().size());
session.delete(product);
session.beginTransaction().commit();
}
}
2.2 多对多 注解版
hibernate.cfg.xml文件添加类:
// .......
<mapping class="com.cn.orm.Factory"></mapping>
<mapping class="com.cn.orm.Idcard"></mapping>
<mapping class="com.cn.orm.Personnel"></mapping>
<mapping class="com.cn.orm.Product"></mapping>
主要是实体类的注解:
Product.java
package com.cn.orm;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
public class Product {
private int proId;
private String proName;
private Double proPrice;
private Set<Factory> factorySet = new HashSet<>();//预防空指针
public Product(){}
public Product(String proName, Double proPrice) {
this.proName = proName;
this.proPrice = proPrice;
}
@Id
@Column(name = "proId")
@GeneratedValue(strategy = GenerationType.IDENTITY)//默认
public int getProId() {
return proId;
} //setter
@Basic
@Column(name = "proName")
public String getProName() {
return proName;
} //setter
@Basic
@Column(name = "proPrice")
public Double getProPrice() {
return proPrice;
} //setter
@ManyToMany(targetEntity=Factory.class,mappedBy="productSet") //让对方维护外键表
@Cascade(CascadeType.ALL)
public Set<Factory> getFactorySet() {
return factorySet;
}
public void setFactorySet(Set<Factory> factorySet) {
this.factorySet = factorySet;
}
}
Factory.java
@ManyToMany(targetEntity=Product.class)
// 使用JoinTabl来描述中间表,并描述中间表中外键与factory,product的映射关系
// joinColumns它是用来描述factory与中间表中的映射关系
// inverseJoinColums它是用来描述product与中间表中的映射关系
@JoinTable(name="product_factory_relation",
joinColumns={@JoinColumn(name="factoryId",referencedColumnName="facId")},
inverseJoinColumns={@JoinColumn(name="productId",referencedColumnName="proId")})
public Set<Product> getProductSet() {
return productSet;
}
3 一对一 案例解析
3.1 xml版本
主:实体类Idcard.java
public class Idcard {
private Integer no;
private String cardId;
private String name;
private Date birthday;
private Personnel personnel;
//getter,setter......
}
Idcard.hbm.xml
<class name="com.cn.orm.Idcard" table="Idcard" schema="crm">
<id name="no">
<column name="no" sql-type="int(11)"/>
<generator class="native"></generator>
</id>
<property name="name">
<column name="name" sql-type="varchar(64)" length="64"/>
</property>
<property name="cardId">
<column name="cardId" sql-type="varchar(18)"/>
</property>
<property name="birthday">
<column name="birthday" sql-type="date"/>
</property>
<!--一对一-->
<!--属性 对方的与当前 this 关联的属性名称 property-ref -->
<one-to-one name="personnel" class="com.cn.orm.Personnel" property-ref="idcard">
</one-to-one>
</class>
从:实体类Personnel.java
public class Personnel {
private Integer id;
private Double height;
private Double weight;
private String headPath; // 头像路径
private Idcard idcard;
//getter,setter......
}
Personnel.hbm.xml
<class name="com.cn.orm.Personnel" table="Personnel" schema="crm">
<id name="id">
<column name="id" sql-type="int(11)"/>
<generator class="native"></generator>
</id>
<property name="height">
<column name="height" sql-type="double" />
</property>
<property name="weight">
<column name="weight" sql-type="double" />
</property>
<property name="headPath">
<column name="headPath" sql-type="varchar(225)"/>
</property>
<!--一对一 column 外键字段列明-->
<many-to-one name="idcard" class="com.cn.orm.Idcard"
column="cardNo" unique="true" not-null="true">
</many-to-one>
</class>
测试类
3.2 一对一 注解版
Idcard.java
private int no;
private String name;
private String cardId;
private Date birthday;
private Personnel personnel;
@Id
@Column(name = "no")
@GeneratedValue(strategy = GenerationType.IDENTITY)//默认
public int getNo() {
return no;
}
// ......
@OneToOne(targetEntity = Personnel.class)
@JoinColumn(name="id",referencedColumnName = "id",unique = true)
public Personnel getPersonnel() {
return personnel;
}
Personnel.java
private int id;
private Double height;
private Double weight;
private String headPath;
private Idcard idcard;
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)//默认
public int getId() {
return id;
}
// ......
@OneToOne(targetEntity = Idcard.class,mappedBy = "personnel")
public Idcard getIdcard() {
return idcard;
}
测试类
注解
1.@Entity
表示这个类是一个映射到数据库中的实体类,可以修改名字
2.@Id
表示这一列为主键列
3.@GeneratedValue
设置主键生成策略,默认是自动增长
4.@Column
设置次列的各种属性:名字,类型,是否非空,是否唯一等等
5.Temporal
设置日期格式
6.@OneToOne
加在映射对象属性上面。表示关系是一对一。
如果加在主键表的对应的外键对象,要加入mappedBy,类似于property-ref
如果加在外键表对应的主键对象上面,需要再配置@JoinColumn(name=”外键列的名字”),如果不设置,默认是表名_Id
7.@ManyToOne
配置在外键表对应的主键属性头部
8.@OneToMany
配置在主键表对应的键属性头部
11. @JoinColumn
设置外键
9.@ManyToMany
两个主键表都会加上。任选一方加入mapperBy属性
10.@JoinTable
为了解决多对多中间表名字和列名的问题,可以在这个注解中配置
11.@Cache
设置缓存
注意:
1.from 名字
如果注解修改了表名,from 表名
如果没有,from 类名
2.级联删除
cascade = CascadeType.ALL cascade = CascadeType.REMOVE
一般加在主表对应的外键属性上面
3.fetch查询
需要自己查询默认的属性
EAGER:查询对象的时候,将关联对象也查询出来
LAZY:查询对象的时候,不查询关联对象