hibernate第二天

hibernate持久化类状态

持久化类就是一个实体类与数据库建立了映射。
hibernate为了方便管理持久化类,将持久化类分成三种状态
1、瞬时态 transient
2、持久态 persistent
3、托管态 detached

瞬时态 transient

特点:持久化对象没有唯一标识OID,没有纳入session的管理
获得:Book book = new Book();
--->持久:session.save(book);
--->托管:book.setId(id);

持久态 persistent

持久态对象有唯一的标识OID,已经纳入session管理
持久化持久态对象具有自动更新数据库的能力。
获取:session.get()/load()/find()/itereate()
--->瞬时:session.delete(book),也称删除态,被删除的对象不建议使用
--->托管:session.close()/clear()/evict();

托管态 detached

获得:Book book = new Book();
book.setId(id);
--->持久:session.update();
--->瞬时 book.setId(null)
托管态对象有唯一的标识OID,没有纳入session管理

对象的状态

持久态对象具有自动更新数据库的能力,依赖与hibernate的一级缓存

hinernate的一级缓存:

什么是缓存:将数据库/硬盘中的文件,数据,放入缓存中(内存中的一块空间),当再次使用,可以直接从内存中获取数据。
hibernate分成两个基本缓存:
1、一级缓存:Session级别的缓存,一级缓存与session的生命周期一致,自带的,不用卸载。
在session中包含一系列的java集合,这些java集合构成了session的缓存,只要session实例没有结束生命周期,存放在缓存中的对象就一直存在。
当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再使用该对象,只要缓存不清空,该对象仍处于生命后期中,当试图get(),load()对象时,会判断缓存中是否存在该对象,有则返回,此时不用查询数据库,没有则查询数据库。
session能够在某些时间点,按照缓存中对象的变化来执行相关的sql语句,来同步更新数据库,这一过程被称为刷出缓存。
默认情况下session在以下时间点刷出数据库:
当程序调用eseeion的commit()方法时,该方法先刷出缓存(session.flush()),然后再向数据库提交事物(tx.commit())
当应用程序执行一些查询语句时,如果缓存中持久化对象的属性已经发生类变化,会先刷出缓存,以保证查询结果能够持久化对象的最新状态,调用session的flush()方法。
深入理解一级缓存区:
一级缓存去session中存在快照去(map集合),当执行save等方法的时候,会将数据存到快照区中(key和value各存一份儿),当调用对象(实例)的set方法时会改变快照区中key的值,然后调用save等方法的时候,会对比kay和value中的值是否相等,如果不相等则发送sql进行数据更新,如果相等则不发送sql更新。

快照区工作图

2、二级缓存:sessionFactory级别的缓存,不是自带的

hibernate管理一级缓存

session.clear():清空一级缓存中的所有对象。
session.evict(User):清空一级缓存中的某一个对象。
session.flush():刷出缓存,发出sql语句
session.refresh():将快照区value的数据覆盖一级缓存的数据。在调用set方法时候,会改变快照区中的key值,当调用此方法,会使用value的数据覆盖key中的数据.
一级缓存刷出的时机:
使用session.setFlushMode(FlushMode.AUTO)指定
常量:ALWAYS:每次查询都会刷出,调用flush(),事物提交都会刷出
AUTO:有些查询会刷出,手动调用flush,事物提交时候
COMMIT:在事物提交的时候,手动调用flush的时候
MANUAL:只有手动调用flush刷出

操作持久化对象的方法

save():保存一条记录,将瞬时态对象转化为持久化对象
saveOrUpdate():对于此方法,当设置了对象的id(托管状态),此时将执行updte()操作,当设置的id不存在,将会报错,如果在映射文件中设置id标签的属性unsaved-value="id值",当设置的id值不存在就会执行保存操作。当没有设置id(瞬时状态),将执行保存操作。
update():执行update语句的时候,就会发送sql,并更新数据库中的数据,值相同的情况下将会执行sql覆盖数据。可以设置映射文件中的class标签的属性select-before-update="true",在更新前将会判断快照中的key和value值,值相同将不发送sql更新。
delete():将持久态转化为瞬时态
get()/load():直接获得持久态的对象。

实体间的关系

1、一对多:
创建原则:在多的一方创建一个字段,作为外建,指向一的一方。
2、多对多:
创建原则:创建第三张表,表中至少有两个字段,分别作为外建指向多对多双方主键
3、一对一:
创建原则:
唯一外键:假设其中一张表为多的一方,创建一对多的关系,在多的一方的外建上设置唯一约束
主键对应:一对一的双方通过主键进行关联

hibernate中一对多的配置

1、创建两个实体(以客户和订单为例)

public class Customer {
    private int cid;
    private String name;
    private Set<Order> orders = new HashSet<Order>();
    
    
    public int getCid() {
        return cid;
    }
    public void setCid(int cid) {
        this.cid = cid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Set<Order> getOrders() {
        return orders;
    }
    public void setOrders(Set<Order> orders) {
        this.orders = orders;
    }
    
}


public class Order {
    private int oid;
    private Customer customer;
    
    
    public int getOid() {
        return oid;
    }
    public void setOid(int oid) {
        this.oid = oid;
    }
    public Customer getCustomer() {
        return customer;
    }
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }
    
}

2、创建映射

1、创建少的一方的映射(Custom)

<?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.bxp.domain.Customer" table="customer" select-before-update="true">
            <!-- 配置唯一标识 -->
            <id name="id" column="id" unsaved-value="-1">
                <generator class="native"></generator>
            </id>
            <!-- 配置普通属性-->
            <property name="name" column="name" length="30"></property>
        
            <!-- 配置集合 -->
            <set name="orders">
            <!-- column的值为多的一方的外建列字段 -->
                <key column="customer"></key>
                <!-- class代表多的一方的类的全路径-->
                <one-to-many class="com.bxp.domain.Order"/>
            </set>
        </class>
    </hibernate-mapping>

2、创建多的一方的映射(Order)

<?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.bxp.domain.Order" table="orders" select-before-update="true">
            <!-- 配置唯一标识 -->
            <id name="id" column="id" unsaved-value="-1">
                <generator class="native"></generator>
            </id>
            <!-- 配置普通属性 -->
            <property name="name" column="name" length="30"></property>
            <!-- 配置映射 -->
            <many-to-one name="customer" column="customer" class="com.bxp.domain.Customer"></many-to-one>
        </class>
    </hibernate-mapping>

hibernate中的级联保存

配置级联之后,只需要保存一个对象即可实现另一个对象的保存
级联具有方向性
保存客户,级联订单

<!-- 配置集合 设置cascade属性配置级联 -->
            <set name="orders" cascade="save-update">
            <!-- column的值为多的一方的外建列字段 -->
                <key column="cum"></key>
                <!-- class代表多的一方的类的全路径-->
                <one-to-many class="com.bxp.domain.Order"/>
            </set>

保存订单级联客户

<many-to-one cascade="save-update" name="customer" column="cum" class="com.bxp.domain.Customer"/>

hibernate中的级联删除

级联删除中,必须先进性查询,在进行删除
级联删除也具有方向性。
配置级联删除客户同时删除订单

<set name="orders" cascade="save-update, delete" inverse="true">
            <!-- column的值为多的一方的外建列字段 -->
                <key column="cum"></key>
                <!-- class代表多的一方的类的全路径-->
                <one-to-many class="com.bxp.domain.Order"/>
            </set>

级联取值

none:不使用级联
save-update:保存或更新的时候级联
delete:删除的时候级联
all:除了孤儿删除以外所有的级联
delete-orphan:孤儿删除,仅限于一对多的情况,只有一对多才会有主从表的关系。一为主,多为从。如当一个客户与订单解除了关系,将外建置为null,此时订单没有了所属的客户,此时会将订单删除。
all-delete-orphan:包含所有的级联

创建数据表时双向维护

双向维护(客户中维护订单信息,订单中维护客户信息),当数据修改时会产生多余的sql。

双向维护产生多余的sql

解决办法:配置inverse=“true”:在哪一端配置,那么哪一端就会放弃外建的维护权,一般的情况下一的一端放弃(在customer就行配置)
cascade:操作关联对象
inverse:控制外建的维护

hibernate中的多对多

创建学生实体

public class Student {
    private int sid;
    private String sname;
    private Set<Course> courses = new HashSet<Course>();
    public int getSid() {
        return sid;
    }
    public void setSid(int sid) {
        this.sid = sid;
    }
    public String getSname() {
        return sname;
    }
    public void setSname(String sname) {
        this.sname = sname;
    }
    public Set<Course> getCourses() {
        return courses;
    }
    public void setCourses(Set<Course> courses) {
        this.courses = courses;
    }
}

创建课程实体

public class Course {
    private int cid;
    private String cname;
    
    private Set<Student> students = new HashSet<Student>();

    public int getCid() {
        return cid;
    }
    public void setCid(int cid) {
        this.cid = cid;
    }
    public String getCname() {
        return cname;
    }
    public void setCname(String cname) {
        this.cname = cname;
    }
    public Set<Student> getStudents() {
        return students;
    }
    public void setStudents(Set<Student> students) {
        this.students = students;
    }
}

配置学生映射关系

<?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.bxp.domain.Student" table="student" select-before-update="true">
            <id name="sid" column="sid" unsaved-value="-1">
                <generator class="native"></generator>
            </id>
            <property name="sname" column="sname" length="30"></property>
            <!-- set标签中的name对应当前实体实体中的课程集合的名称  table:中间表的名称 -->
            <set name="courses" table="student_course">
                <!-- key中的column代表当前类在中间表中的外建 -->
                <key column="sid"></key>
                <!--many-to-many中的class代表另一类的全路径  column代表另一对象在中间表中的外建名称  -->
                <many-to-many class="com.bxp.domain.Course" column="cid"></many-to-many>
            </set>
        </class>
    </hibernate-mapping>

配置课程映射关系

<?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.bxp.domain.Course" table="course" select-before-update="true">
            <id name="cid" column="cid" unsaved-value="-1">
                <generator class="native"></generator>
            </id>
            <property name="cname" column="cname" length="30"></property>
        
            <!-- set标签中的name对应当前实体中学生集合的名称  table:中间表的名称 -->
            <set name="students" table="student_course">
                <!-- key中的column代表当前类在中间表中的外建 -->
                <key column="cid"></key>
                <!--many-to-many中的class代表另一类的全路径  column代表另一对象在中间表中的外建名称  -->
                <many-to-many class="com.bxp.domain.Student" column="sid"></many-to-many>
            </set>
        </class>
    </hibernate-mapping>

多对多关系如果两张表都控制中间表的外建,添加数据的时候会报错,因为会两次向中见表中添加数据,会出现向同主键重复的问题,所以需要让其中一方放弃对外建的维护,一般是从表放弃对外建的维护(学生选课,学生为主表,课程为从表)
多对多可以进行级联操作,但是多对多中的级联删除很少使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容