- 目前企业级应用一般采用面向对象的开发方法,而内存中的对象数据不能永久存在,如果想用关系型数据库来永久保存这些数据的话,就需要有一个对象-关系的映射过程。在这种情况下,有了许多解决对象持久化的中间件,Hibernate便是非常成功的的一个开源框架;
- Hibernate是一种ORM框架,在Java对象与关系数据库之间建立某种映射,以实现直接存取Java对象(POJO);
- 对象-关系映射ORM是用来将对象和对象之间的关系对应到数据库表与表之间关系的一种模式;
持久化:把内存中数据同步保存到数据库中或永久存储设备中;
持久层:专门负责持久化工作的逻辑层,由它统一与数据库层打交道;
Hibernate 应用程序体系结构视图:
涉及的类对象:
SessionFactory 对象:
一个线程安全的Session工厂类,能为不同的线程生成不同的Session.每个数据库需要一个 SessionFactory 对象使用一个单独的配置文件。所以如果你使用多种数据库那么你要创造多种 SessionFactory 对象。SessionFactory 维护着Session相关的资源,包括数据库连接池等;
Session 对象:
一个会话被用于与数据库的物理连接。Session 对象是轻量级的,并被设计为每次实例化都需要与数据库的交互。持久对象通过 Session 对象保存和检索。Session 对象不应该长时间保持开启状态因为它们通常情况下并非线程安全,并且它们应该按照所需创造和销毁。
Transaction 对象:
Transaction代表一次事务,事务内包含若干的数据修改,事务提交后才生效。如果事务失败或者回滚,所有的修改都会失效。Hibernate的事务不同于数据库的事务。
Hibernate配置:
S.N. | 属性和描述 |
---|---|
1 | hibernate.dialect 这个属性使 Hibernate 应用为被选择的数据库生成适当的 SQL。 |
2 | hibernate.connection.driver_class JDBC 驱动程序类。 |
3 | hibernate.connection.url 数据库实例的 JDBC URL。 |
4 | hibernate.connection.username 数据库用户名。 |
5 | hibernate.connection.password 数据库密码。 |
6 | hibernate.connection.pool_size 限制在 Hibernate 应用数据库连接池中连接的数量。 |
7 | hibernate.connection.autocommit 允许在 JDBC 连接中使用自动提交模式。 |
会话:
Session 用于获取与数据库的物理连接。 Session 对象是轻量级的,并且设计为在每次需要与数据库进行交互时被实例化。持久态对象被保存,并通过 Session 对象检索找回。该 Session 对象不应该长时间保持开放状态,因为它们通常不能保证线程安全,而应该根据需求被创建和销毁。Session 的主要功能是为映射实体类的实例提供创建,读取和删除操作。这些实例可能在给定时间点时存在于以下三种状态之一:
- 临时状态:对象在保存进数据库之前为临时状态。这时候数据库中没有该对象的信息,该对象的Id属性也为空。如果没有被持久化,程序退出时临时状态的对象信息会丢失,即在程序中new一个对象但是还未与Session关联;
- 持久化状态:对象在保存进数据库后或者从数据库中加载后、并且没有脱离Session时为持久化状态。这时数据库中有该对象的信息,该对象的Id为数据库中对应记录的主键值。由于还在Session中,持久化状态的对象可以执行任何有关数据库的操作;
- 脱管状态:对象曾经处于持久化状态、但是现在已经离开Session了,即session.close()。虽然脱管状态的对象有Id值,有对应的数据库记录,但是已经无法执行有关数据库的操作;
Session 接口方法:
序号 | Session 方法及说明 |
---|---|
1 | Transaction beginTransaction() 开始工作单位,并返回关联事务对象。 |
2 | void cancelQuery() 取消当前的查询执行。 |
3 | void clear() 完全清除该会话。 |
4 | Connection close() 通过释放和清理 JDBC 连接以结束该会话。 |
5 | Criteria createCriteria(Class persistentClass) 为给定的实体类或实体类的超类创建一个新的 Criteria 实例。 |
6 | Criteria createCriteria(String entityName) 为给定的实体名称创建一个新的 Criteria 实例。 |
7 | Serializable getIdentifier(Object object) 返回与给定实体相关联的会话的标识符值。 |
8 | Query createFilter(Object collection, String queryString) 为给定的集合和过滤字符创建查询的新实例。 |
9 | Query createQuery(String queryString) 为给定的 HQL 查询字符创建查询的新实例。 |
10 | SQLQuery createSQLQuery(String queryString) 为给定的 SQL 查询字符串创建 SQLQuery 的新实例。 |
11 | void delete(Object object) 从数据存储中删除持久化实例。 |
12 | void delete(String entityName, Object object) 从数据存储中删除持久化实例。 |
13 | Session get(String entityName, Serializable id) 返回给定命名的且带有给定标识符或 null 的持久化实例(若无该种持久化实例)。 |
14 | SessionFactory getSessionFactory() 获取创建该会话的 session 工厂。 |
15 | void refresh(Object object) 从基本数据库中重新读取给定实例的状态。 |
16 | Transaction getTransaction() 获取与该 session 关联的事务实例。 |
17 | boolean isConnected() 检查当前 session 是否连接。 |
18 | boolean isDirty() 该 session 中是否包含必须与数据库同步的变化? |
19 | boolean isOpen() 检查该 session 是否仍处于开启状态。 |
20 | Serializable save(Object object) 先分配一个生成的标识,以保持给定的瞬时状态实例。 |
21 | void saveOrUpdate(Object object) 保存(对象)或更新(对象)给定的实例。 |
22 | void update(Object object) 更新带有标识符且是给定的处于脱管状态的实例的持久化实例。 |
23 | void update(String entityName, Object object) 更新带有标识符且是给定的处于脱管状态的实例的持久化实例。 |
Get()和Load()区别:
- Load():当使用load方法来得到一个对象时,此时hibernate会使用延迟加载的机制来加载这个对象,即:当我们使用session.load()方法来加载一个对象时,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,只有当我们要使用这个对象,得到其它属性时,这个时候才会发出sql语句,从数据库中去查询我们的对象;
- Get():相对于load的延迟加载方式,get就直接的多,当我们使用session.get()方法来得到一个对象时,不管我们使不使用这个对象,此时都会发出sql语句去从数据库中查询出来;
- 在缓存中没有对象时,get返回的是pojo对象,load返回的是代理对象;
Hibernate实体映射:
Hibernate实体映射的主要任务是实现数据库关系表与持久类之间的映射;
映射类型:
映射类型 | Java 类型 | ANSI SQL 类型 |
---|---|---|
integer | int 或 java.lang.Integer | INTEGER |
long | long 或 java.lang.Long | BIGINT |
short | short 或 java.lang.Short | SMALLINT |
float | float 或 java.lang.Float | FLOAT |
double | double 或 java.lang.Double | DOUBLE |
big_decimal | java.math.BigDecimal | NUMERIC |
character | java.lang.String | CHAR(1) |
string | java.lang.String | VARCHAR |
byte | byte 或 java.lang.Byte | TINYINT |
boolean | boolean 或 java.lang.Boolean | BIT |
yes/no | boolean 或 java.lang.Boolean | CHAR(1) ('Y' or 'N') |
true/false | boolean 或 java.lang.Boolean | CHAR(1) ('T' or 'F') |
注解:
@Entity:实体类;@Table:对应的表;@Id:主键;@Column:普通属性;
package com.answer.po;
import javax.persistence.*;
import java.util.Objects;
@Entity
public class User {
private int id;
private String username;
private String password;
@Id
@Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Basic
@Column(name = "username")
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Basic
@Column(name = "password")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return id == user.id &&
Objects.equals(username, user.username) &&
Objects.equals(password, user.password);
}
@Override
public int hashCode() {
return Objects.hash(id, username, password);
}
}
hibernate.cfg.xml:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:mysql://localhost:3306/bz?useUnicode=true&characterEncoding=utf-8&useSSL=true</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<mapping class="com.answer.po.User" ></mapping>
<mapping class="com.answer.po.User2"></mapping>
<!-- DB schema will be updated if needed -->
<!-- <property name="hbm2ddl.auto">update</property> -->
</session-factory>
</hibernate-configuration>
xml:
实体类:
package com.answer.po;
import java.util.Objects;
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return id == user.id &&
Objects.equals(username, user.username) &&
Objects.equals(password, user.password);
}
@Override
public int hashCode() {
return Objects.hash(id, username, password);
}
}
hibernate.hbm.xml:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.answer.po">
<class name="User" table="user">
<id name="id" column="id"><!-- 主键-->
<generator class="native"></generator><!-- 数据库自增长-->
</id>
<property name="username" type="string" column="username"></property>
<property name="password" type="string" column="password"></property>
</class>
</hibernate-mapping>
hibernate.cfg.xml:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:mysql://localhost:3306/bz?useUnicode=true&characterEncoding=utf-8&useSSL=true</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<mapping resource="hibernate.hbm.xml"></mapping>
<!-- DB schema will be updated if needed -->
<!-- <property name="hbm2ddl.auto">update</property> -->
</session-factory>
</hibernate-configuration>
Hibernate实体关系映射:
cascade(级联):级联在编写触发器时经常用到,触发器的作用是当 主控表信息改变时,用来保证其关联表中数据同步更新。若对触发器来修改或删除关联表相记录,必须要删除对应的关联表信息,否则,会存有脏数据。所以,适当的做法是,删除主表的同时,关联表的信息也要同时删除,在hibernate中,只需设置cascade属性值即可;
- CascadeType.PERSIST:级联新增(又称级联保存):对order对象保存时也对items里的对象也会保存。对应EntityManager的presist方法
例子:只有A类新增时,会级联B对象新增。若B对象在数据库存(跟新)在则抛异常(让B变为持久态)
- CascadeType.MERGE:级联合并(级联更新):若items属性修改了那么order对象保存时同时修改items里的对象。对应EntityManager的merge方法
例子:指A类新增或者变化,会级联B对象(新增或者变化)
- CascadeType.REMOVE:级联删除:对order对象删除也对items里的对象也会删除。对应EntityManager的remove方法
例子:REMOVE只有A类删除时,会级联删除B类;
- CascadeType.REFRESH:级联刷新:获取order对象里也同时也重新获取最新的items时的对象。对应EntityManager的refresh(object)方法有效。即会重新查询数据库里的最新数据 (用的比较少)
- CascadeType.ALL:以上四种都是
一对一:
@OneToOne(cascade = {CascadeType.ALL})
Hus1实体类:
package com.answer.po;
import javax.persistence.*;
import java.util.Objects;
@Entity
public class Hus1 {
private int hid;
private String hname;
private String hpwd;
private Wife1 wife1;
@Id
@Column(name = "hid")
public int getHid() {
return hid;
}
public void setHid(int hid) {
this.hid = hid;
}
@Basic
@Column(name = "hname")
public String getHname() {
return hname;
}
public void setHname(String hname) {
this.hname = hname;
}
@Basic
@Column(name = "hpwd")
public String getHpwd() {
return hpwd;
}
public void setHpwd(String hpwd) {
this.hpwd = hpwd;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Hus1 hus1 = (Hus1) o;
return hid == hus1.hid &&
Objects.equals(hname, hus1.hname) &&
Objects.equals(hpwd, hus1.hpwd);
}
@Override
public int hashCode() {
return Objects.hash(hid, hname, hpwd);
}
@OneToOne(cascade = {CascadeType.ALL})//设置级联
@JoinColumn(name = "hid", referencedColumnName = "wid", nullable = false)
public Wife1 getWife1() {
return wife1;
}
public void setWife1(Wife1 wife1) {
this.wife1 = wife1;
}
}
Wife1实体类:
package com.answer.po;
import javax.persistence.*;
import java.util.Objects;
@Entity
public class Wife1 {
private int wid;
private String wname;
private String wpwd;
private Hus1 hus1;
@Id
@Column(name = "wid")
public int getWid() {
return wid;
}
public void setWid(int wid) {
this.wid = wid;
}
@Basic
@Column(name = "wname")
public String getWname() {
return wname;
}
public void setWname(String wname) {
this.wname = wname;
}
@Basic
@Column(name = "wpwd")
public String getWpwd() {
return wpwd;
}
public void setWpwd(String wpwd) {
this.wpwd = wpwd;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Wife1 wife1 = (Wife1) o;
return wid == wife1.wid &&
Objects.equals(wname, wife1.wname) &&
Objects.equals(wpwd, wife1.wpwd);
}
@Override
public int hashCode() {
return Objects.hash(wid, wname, wpwd);
}
@OneToOne(mappedBy = "wife1")//指定了一对一的关系,mappedBy指定由对方来维护关联关系
public Hus1 getHus1() {
return hus1;
}
public void setHus1(Hus1 hus1) {
this.hus1 = hus1;
}
}
一对多:
@OneToMany(mappedBy = "dage",cascade = {CascadeType.ALL})
Dage实体类:
package com.answer.dao;
import com.answer.po.Dage;
import com.answer.po.Xiaodi;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
import java.util.List;
import java.util.Set;
public class DageDAO {
private Configuration configuration;
private SessionFactory sessionFactory;
private Session session;
public DageDAO(){
configuration=new Configuration().configure("hibernate.cfg.xml");
sessionFactory=configuration.buildSessionFactory();
}
@Test
public void update(){
session=sessionFactory.openSession();
Transaction transaction=session.beginTransaction();
try{
Dage dage=session.get(Dage.class,1);
dage.setName("DAGE");
session.update(dage);
transaction.commit();
}catch (Exception e){
transaction.rollback();
e.printStackTrace();
}finally {
session.close();
}
}
@Test
public void insert(){
session=sessionFactory.openSession();
Transaction transaction=session.beginTransaction();
try{
Dage dage=new Dage();
dage.setId(1);
dage.setName("da1");
Xiaodi xiaodi=new Xiaodi();
xiaodi.setId(1);
xiaodi.setName("xiao1");
xiaodi.setDage(dage);
Xiaodi xiaodi1=new Xiaodi();
xiaodi1.setId(2);
xiaodi1.setName("xiao2");
xiaodi1.setDage(dage);
dage.getXiaodis().add(xiaodi);
dage.getXiaodis().add(xiaodi1);
session.save(dage);
transaction.commit();
}catch (Exception e){
transaction.rollback();
e.printStackTrace();
}finally {
session.close();
}
}
@Test
public void del(){
session=sessionFactory.openSession();
Transaction transaction=session.beginTransaction();
try{
Dage dage=session.get(Dage.class,3);
Set<Xiaodi> xiaodis=dage.getXiaodis();
session.delete(dage);
transaction.commit();
}catch (Exception e){
transaction.rollback();
e.printStackTrace();
}finally {
session.close();
}
}
@Test
public void query(){
session=sessionFactory.openSession();
try{
List<Dage> list=session.createQuery("from Dage ").list();
for(Dage dage:list){
System.out.print(dage.getName()+":");
Set<Xiaodi> set=dage.getXiaodis();
set.forEach(xiaodi -> System.out.println(xiaodi.getName()));
}
}catch (Exception e){
e.printStackTrace();
}finally {
session.close();
}
}
}
Xiaodi实体类:
package com.answer.po;
import javax.persistence.*;
import java.util.Objects;
@Entity
public class Xiaodi {
private int id;
private String name;
private Dage dage;
@Id
@Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Basic
@Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Xiaodi xiaodi = (Xiaodi) o;
return id == xiaodi.id &&
Objects.equals(name, xiaodi.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
@ManyToOne
@JoinColumn(name = "dage_id", referencedColumnName = "id")
public Dage getDage() {
return dage;
}
public void setDage(Dage dage) {
this.dage = dage;
}
}
多对多:
需要创建一个中间表(包含其他两个表主键的外键);
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE),增加和修改时做联动,删除时不做;
Tea实体类:
package com.answer.po;
import org.hibernate.annotations.Cascade;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@Entity
public class Tea {
private int tid;
private String tname;
private Set<Stu> stus=new HashSet<>();
@Id
@Column(name = "tid")
public int getTid() {
return tid;
}
public void setTid(int tid) {
this.tid = tid;
}
@Basic
@Column(name = "tname")
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tea tea = (Tea) o;
return tid == tea.tid &&
Objects.equals(tname, tea.tname);
}
@Override
public int hashCode() {
return Objects.hash(tid, tname);
}
@ManyToMany
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
@JoinTable(name = "teaandstu", catalog = "", schema = "bz", joinColumns = @JoinColumn(name = "tid", referencedColumnName = "tid", nullable = false), inverseJoinColumns = @JoinColumn(name = "sid", referencedColumnName = "sid", nullable = false))
public Set<Stu> getStus() {
return stus;
}
public void setStus(Set<Stu> stus) {
this.stus = stus;
}
}
Stu实体类:
package com.answer.po;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@Entity
public class Stu {
private int sid;
private String sname;
private Set<Tea> teas=new HashSet<>();
@Id
@Column(name = "sid")
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
@Basic
@Column(name = "sname")
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Stu stu = (Stu) o;
return sid == stu.sid &&
Objects.equals(sname, stu.sname);
}
@Override
public int hashCode() {
return Objects.hash(sid, sname);
}
@ManyToMany(mappedBy = "stus")
public Set<Tea> getTeas() {
return teas;
}
public void setTeas(Set<Tea> teas) {
this.teas = teas;
}
}