Hibernate

day1

Hibernate是开源的ORM(对象关系映射)框架。对JDBC进行轻量级的封装,将pojo和数据库表建立映射关系,是一个全自动的orm框架。它可以自动生成sql语句,自动执行。使码农可以使用面向对象的思想来操作数据库。

版本是5.0.7

hibernate
英 [ˈhaɪbəneɪt] 美 [ˈhaɪbərneɪt]
vi.
(某些动物)冬眠,蛰伏

解压后的目录:documentation:文档,API文档
lib:Hibernate编译和运行所依赖的JAR包。required子目录下的是运行hibernate必须的jar包。

书写主配置文件 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>
        <!-- 
        #hibernate.dialect org.hibernate.dialect.MySQLDialect
        #hibernate.dialect org.hibernate.dialect.MySQLInnoDBDialect
        #hibernate.dialect org.hibernate.dialect.MySQLMyISAMDialect
        #hibernate.connection.driver_class com.mysql.jdbc.Driver
        #hibernate.connection.url jdbc:mysql:///test
        #hibernate.connection.username gavin
        #hibernate.connection.password
         -->
         <!-- 数据库驱动 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
         <!-- 数据库url -->
        <property name="hibernate.connection.url">jdbc:mysql:///student</property>
         <!-- 数据库连接用户名 -->
        <property name="hibernate.connection.username">root</property>
         <!-- 数据库连接密码 -->
        <property name="hibernate.connection.password">root</property>
        <!-- 数据库方言
            不同的数据库中,sql语法略有区别. 指定方言可以让hibernate框架在生成sql语句时.针对数据库的方言生成.
            sql99标准: DDL 定义语言  库表的增删改查
                      DCL 控制语言  事务 权限
                      DML 操纵语言  增删改查
            注意: MYSQL在选择方言时,请选择最短的方言.
         -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>


        <!-- #hibernate.show_sql true 
             #hibernate.format_sql true
        -->
        <!-- 将hibernate生成的sql语句打印到控制台 -->
        <property name="hibernate.show_sql">true</property>
        <!-- 将hibernate生成的sql语句格式化(语法缩进) -->
        <property name="hibernate.format_sql">true</property>
        <!-- 
        ## auto schema export  自动导出表结构. 自动建表
        #hibernate.hbm2ddl.auto create      自动建表.每次框架运行都会创建新的表.以前表将会被覆盖,表数据会丢失.(开发环境中测试使用)
        #hibernate.hbm2ddl.auto create-drop 自动建表.每次框架运行结束都会将所有表删除.(开发环境中测试使用)
        #hibernate.hbm2ddl.auto update(推荐使用) 自动生成表.如果已经存在不会再生成.如果表有变动.自动更新表(不会删除任何数据).
        #hibernate.hbm2ddl.auto validate    校验.不自动生成表.每次启动会校验数据库中表是否正确.校验失败.
         -->
        <property name="hibernate.hbm2ddl.auto">update</property>

         <!-- 指定hibernate操作数据库时的隔离级别 
            #hibernate.connection.isolation 1|2|4|8     
            0001    1   读未提交
            0010    2   读已提交
            0100    4   可重复读
            1000    8   串行化
         -->
         <property name="hibernate.connection.isolation">4</property>

         <!-- 指定session与当前线程绑定 -->
         <property name="hibernate.current_session_context_class">thread</property>

        <!-- 引入orm元数据
            路径书写: 填写src下的路径
         -->
        <mapping resource="com/hero/pojo/Customer.hbm.xml" />
        <mapping resource="com/hero/pojo/LinkMan.hbm.xml" />
        <mapping resource="com/hero/pojo/Role.hbm.xml" />
        <mapping resource="com/hero/pojo/User.hbm.xml" />
    </session-factory>
</hibernate-configuration>

书写orm元数据(pojo和表的映射配置文件)

class Customer { }
Customer.hbm.xml



<hibernate-mapping package="com.hero.pojo" >

<class name="Customer" table="cst_customer" >
<id name="custId" column="cust_id" >
<!-- generator:主键生成策略(明天讲) -->
<generator class="native"></generator>
</id>


<property name="custPhone" column="cust_phone" ></property>


</class>

</hibernate-mapping>












@Test
    public void TestA() {

        //配置加载类,加载主配置,加载ORM元数据;使用空参,加载默认位置(src下的hibernate.cfg.xml)的配置文件
        Configuration conf = new Configuration().configure();
        //1.负责保存和使用所有配置信息,消耗内存很大   2.属于线程安全对象   
        //因为上面两个原因,所有保证在web项目中只有一个sessionFactory
        SessionFactory factory = conf.buildSessionFactory();
        //获得一个新的session
        Session session = factory.openSession();
        //获取并开启事务
        Transaction transaction = session.beginTransaction();

        //---------------------------------------------
        Customer customer = new Customer();
        customer.setCustName("百度");
        session.save(customer);

        //---------------------------------------------
        transaction.commit();
        //transaction.rollback();

        session.close();

        factory.close();

    }

day2

实体的规则

实体类创建注意事项:
1.要有空参构造器(底层是反射)
2.提供set/get,私有参数
private int id; //成员变量

一对set/get方法叫做一个属性,即使set/get是没有意义的,一个类中有多少个属性取决于有多少对set/get

3.持久化类中的属性,最好使用包装类型

4.持久化类需要提供oid和数据库中的主键相对应(没有主键的表是无法使用hibernate的)

5.不要用final修饰class(代理是用cglib继承的,如果用final就无法继承)


主键类型:
1.自然主键(少见,例如身份证号码)
在业务中,某个属性刚好不重复可以代表每个不同的行
2.代理主键(常见)
一个没业务意义的列,但是不重复可以代表不同的行


主键生成策略(其中)
每条记录录入的时候,主键的生成规则
1.identity 主键自增,由数据库来维护主键值,不需要指定主键值

<id name="custId" column="cust_id" >
<generator class="identity"></generator>
</id>

2.increment 主键自增,由hibernate来维护主键,每次插入会查询数据库,然后id+1
开发中不要用,因为有线程安全问题。而且所有的数据库都有自己的维护自增主键的算法,不要外界框架来帮忙

3.sequence:oracle中的主键生成策略
4.hilo:主键自增,由hibernate来维护主键,采用高低位算法

5.native(自动三选一:identity,sequence,hilo)

6.uuid:产生随机字符串,必须要string类型

7.assigned开发人员自己录入,自然主键

hibernate的状态

对象分为三种状态
1.瞬时态:,数据库中没有ID和你对应,没有在session中缓存,失去引用后就会被垃圾回收,永远没有你存在的痕迹
2.持久态:在提交事务后数据库有ID对应,和session有关联
3.游离态|托管状态:有Id对应,没有和session关联

Customer customer = new Customer();//没有id,没有和session关联=>瞬时态

customer.setCustName("百度");//瞬时态
        session.save(customer);//有id,有关联,持久态

        //---------------------------------------------
        transaction.commit();
        //transaction.rollback();

        session.close();//有id,没关联,游离态

        factory.close();

save方法的本质:将瞬时态对象转化成持久态对象
在session.save(customer);前打断点
如果主键生成策略是native,那么在session.save(customer);执行后会打印Insert语句,目的是为了获得ID
但是如果主键生成策略是increment
那么session.save(customer);执行后会打印,目的是为了获得ID

Hibernate: 
    select
        max(cust_id) 
    from
        cst_customer

提交事务后才会打印insert语句

如果是uuid,那么save不会打印任何语句,提交事务后才会打印insert语句


持久化:持久化状态的对象的任何变化都会自动同步到数据库中,使用hibernate的目的就是同步数据库,所以用持久态对象进行增改

Customer c = session.get(Customer.class, "2");
        System.out.println(c);
        c.setCustName("六六六");
        transaction.commit();
        session.close();
        factory.close();

上面的代码修改了c对象的属性,在提交事务后会修改数据库里的名字属性

saveOrUpdate:可以将对象转换成持久态(不管你是游离态还是瞬时态)
save:把瞬时态变成持久态
close:关闭session,把持久态变成游离态
update:把游离态变成持久态
delete:把持久态变成瞬时态
get:获得一个持久态对象


缓存

提高操作数据库的效率
查询数据库活的一个快照
持久态:其实就是在session中有缓存数据
缓存中的对象会同步到数据库中
查询数据先走缓存,如果缓存中有,就不去数据库了
但是如果数据库数据变了,hibernate是不知道的,还是去缓存,结果就会出错

隔离级别

1 0001 读未提交
2 0010 读已提交
4 0100 可重复读
8 1000 串行化

 <property name="hibernate.connection.isolation">4</property>
//获取并开启事务
        Transaction transaction = session.beginTransaction();

事务在service层,需要session
dao层要用session操作数据
两个层的session必须一样

JDBC的时候要手动绑定线程
现在框架sf.getCurrentSession()


使用sf.getCurrentSession
要在主配置文件中配置

<property name="hibernate.current_session_context_class">thread</property>

*注意:***getCurrentSession获得的session提交事务后会自动关闭session,如果你手动close那么就会抛异常

批量查询(概述)

HQL 在查询多表但是不复杂的时候用

Criteria 单表查询

SQL原生态的 适合特别特别复杂的业务


HQL查询

String hql = select 属性 from 对象完整类名

如果是select *,那么可以省略成from 对象完整类名

如果类的名字不重复,那么可以直接写类的名字,不用带包名

在Hql中,是不可以出现任何关于数据库信息的词句,完全面向对象

//基础查询
String hql = "from Customer";
        Query query  = session.createQuery(hql);
        List<Customer> list = query.list(); //返回所有数据列表
        for(Customer c : list){
            System.out.println(c);
        }

        //query.uniqueResult(); 如果你知道只有一条结果
条件查询
String hql = "from Customer where custId = "+"2";
        Query query  = session.createQuery(hql);
        Customer c = (Customer) query.uniqueResult(); 
        System.out.println(c);
问号占位符
String hql = "from Customer where custId = ? ";
        Query query  = session.createQuery(hql);
        query.setString(0, "3");//0代表第一个外号,内容是“3”

        //推荐使用setParameter,不管你是什么类型都可以用
        query.setParameter(0, "3");

        Customer c = (Customer) query.uniqueResult(); 
        System.out.println(c);
//命名占位符
//如果业务使语句?的位置变化,靠顺序就难了
//为数据起一个随便的名字,格式是   :+名字
String hql = "from Customer where custId = :hero ";
        Query query  = session.createQuery(hql);

        //重载方法,赋予名字是hero值“6”
        query.setParameter("hero", "6");
        Customer c = (Customer) query.uniqueResult(); 
        System.out.println(c);
分页查询
String hql = "from Customer  ";
        Query query  = session.createQuery(hql);
        //起始行,从第几行开始抓
        query.setFirstResult(0);
        //一共呢要抓几行数据啊
        query.setMaxResults(3);
        List<Customer> c = query.list();

        System.out.println(c);

criteria 英[kraɪ’tɪərɪə]
美[kraɪˈtɪrɪə]
n. (批评、判断等的) 标准,准则( criterion的名词复数 ); (criterion的复数)

criteria不用写语句,完全面向对象查询。QBC(query by Criteria)

//查询所有Customer对象
        Criteria criteria = session.createCriteria(Customer.class);

        List<Customer> c = criteria.list();
        //criteria.uniqueResult();


        System.out.println(c);
//条件查询
Criteria criteria = session.createCriteria(Customer.class);

        //  Restrictions可以点出来许多sql条件

        criteria.add(Restrictions.eq("custId", "2"));

        Customer c = (Customer) criteria.uniqueResult();


        System.out.println(c);



Restrictions可以点出来许多sql条件
这里写图片描述

这里写图片描述

//分页查询
Criteria criteria = session.createCriteria(Customer.class);

        criteria.setFirstResult(0);
        criteria.setMaxResults(2);
        List<Customer> c  = criteria.list();


        System.out.println(c);
//查询总行数
Criteria criteria = session.createCriteria(Customer.class);
        //projections可以点出聚合函数       
        criteria.setProjection(Projections.rowCount());

        Long c = (Long)criteria.uniqueResult();


        System.out.println(c);

原生态SQL查询

//查询所有Customer对象
        String sql = "select * from cst_customer";
        SQLQuery query = session.createSQLQuery(sql);
        //因为你写的sql,框架不知道你查的是什么类型,
        所有列表里每个数据都是object数组,存放表的每一个属性
        List<Object[]> c = query.list();
        for(Object[] objs : c){
            System.out.println(Arrays.toString(objs));
        }

楼上的方法太麻烦了,框架有解决办法

//查询所有Customer对象
        String sql = "select * from cst_customer";
        SQLQuery query = session.createSQLQuery(sql);
        //告诉框架你的实体类型
        query.addEntity(Customer.class);
        List<Customer> c = query.list();
        System.out.println(c);
//占位符查询
//String sql = "select * from cst_customer limit ?,?";
        String sql = "select * from cst_customer where cust_id = ?";
        SQLQuery query = session.createSQLQuery(sql);
        //告诉框架你的实体类型
        query.addEntity(Customer.class);
        //第一个占位符值是2
        query.setParameter(0, "2");

        Customer c = (Customer) query.uniqueResult();
        System.out.println(c);

day3

这里写图片描述

上图是数据库表的表示方式,那么在POJO中如何表达?


一对一

少见

一对多|多对一

例如
客户表:id=1 name=百度 id=2 name=腾讯
职员表:id =1 name=张三 cid=1

如果POJO表达关系

class company{
private int id;
private String name;
private Set<staff>  staffs;
}

class staff{
private int id;
private String name;
private Company com;

}


//客户类:客户有百度,腾讯,阿里
public class Customer {

    private Long cust_id;

    private String cust_name;
    private String cust_source;
    private String cust_industry;
    private String cust_level;
    private String cust_linkman;
    private String cust_phone;
    private String cust_mobile;

    private Set<LinkMan> linkMans;
   }
<?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 package="com.hero.pojo" >

    <class name="Customer" table="cst_customer" >

        <id name="cust_id"  >
            <generator class="native"></generator>
        </id>

        <property name="cust_name" column="cust_name" ></property>
        <property name="cust_source" column="cust_source" ></property>
        <property name="cust_industry" column="cust_industry" ></property>
        <property name="cust_level" column="cust_level" ></property>
        <property name="cust_linkman" column="cust_linkman" ></property>
        <property name="cust_phone" column="cust_phone" ></property>
        <property name="cust_mobile" column="cust_mobile" ></property>

        <!-- 集合,一对多关系 
            name:类中集合的属性名字
            column:外键列名
            class: 一对多对应的类名字
        -->
        <set name="linkMans" >
            <key column="lkm_cust_id"></key>
            <one-to-many class="LinkMan"/>
        </set>


    </class>
</hibernate-mapping>

//一个客户对象有多个联系人,客户公司是百度,百度的联系人有好多个
public class LinkMan {

    private Long lkm_id;

    private Character lkm_gender;
    private String lkm_name;
    private String lkm_phone;
    private String lkm_email;
    private String lkm_qq;
    private String lkm_mobile;
    private String lkm_memo;
    private String lkm_position;

    //表达多对一关系
    private Customer customer ;
    }
<?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 package="com.hero.pojo" >
    <class name="LinkMan" table="cst_linkman" >
        <id name="lkm_id"  >
            <generator class="native"></generator>
        </id>
        <property name="lkm_gender"  ></property>
        <property name="lkm_name"  ></property>
        <property name="lkm_phone"  ></property>
        <property name="lkm_email"  ></property>
        <property name="lkm_qq"  ></property>
        <property name="lkm_mobile"  ></property>
        <property name="lkm_memo"  ></property>
        <property name="lkm_position"  ></property>

        <!-- 多对一 -->
        <!-- 
            name属性:引用属性名
            column属性: 外键列名
            class属性: 与我关联的对象完整类名
         -->
          <!-- 
            级联操作:   cascade
                save-update: 级联保存更新
                delete:级联删除
                all:save-update+delete
            级联操作: 简化操作.目的就是为了少些两行代码.
          -->
          <!-- 多的一方: 不能放弃维护关系的.外键字段就在多的一方.  -->
        <many-to-one name="customer" column="lkm_cust_id" class="Customer"  >
        </many-to-one>
    </class>
</hibernate-mapping>

@Test
    //保存客户 以及客户 下的联系人
    public void fun1(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 开启事务
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        //3操作
        Customer c = new Customer();
        c.setCust_name("百度");

        LinkMan lm1 = new LinkMan();
        lm1.setLkm_name("李彦宏");

        LinkMan lm2 = new LinkMan();
        lm2.setLkm_name("陆奇");

        //表达一对多,客户下有多个联系人
        c.getLinkMans().add(lm1);
        c.getLinkMans().add(lm2);

        //表达对对对,联系人属于哪个客户
        lm1.setCustomer(c);
        lm2.setCustomer(c);


        session.save(c);
        session.save(lm1);
        session.save(lm2);

        //-------------------------------------------------
        //4提交事务
        tx.commit();
        //5关闭资源
        session.close();
    }

@Test
    //为客户增加联系人
    public void fun2(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 开启事务
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        //3操作
        //1> 获得要操作的客户对象
        Customer c = session.get(Customer.class,2L);
        //2> 创建联系人
        LinkMan lm1 = new LinkMan();
        lm1.setLkm_name("张三");
        //3> 将联系人添加到客户,将客户设置到联系人中
        c.getLinkMans().add(lm1);
        lm1.setCustomer(c);
        //4> 执行保存
        session.save(lm1);
        //-------------------------------------------------
        //4提交事务
        tx.commit();
        //5关闭资源
        session.close();
    }


级联操作:save-update,不要用delete,太危险了

@Test
    //为客户删除联系人,这里只是解除了张三作为百度联系人的属性,数据库中还是有张三的记录的
    //只是张三的外键变成了空
    public void fun3(){
        //1 获得session
        Session session = HibernateUtils.openSession();
        //2 开启事务
        Transaction tx = session.beginTransaction();
        //-------------------------------------------------
        Customer c = session.get(Customer.class, 2L);

        LinkMan linkMan = session.get(LinkMan.class, 5L);

        c.getLinkMans().remove(linkMan);

        linkMan.setCustomer(null);

        //-------------------------------------------------
        //4提交事务
        tx.commit();
        //5关闭资源
        session.close();
    }

//上面保存新增的百度联系人
//如果新增了100个,难道要一个个save么?
 session.save(c);
        session.save(lm1);
        session.save(lm2);
<!-- 
            级联操作:   cascade
                save-update: 级联保存+更新
                delete:级联删除
                all:save-update+delete
            级联操作: 简化操作.目的就是为了少些两行代码.
            保存业务公司的时候,自动更新联系人信息
          -->
        <set name="linkMans" cascade="save-update" >
            <key column="lkm_cust_id"></key>
            <one-to-many class="LinkMan"/>
        </set>

同样的,在多的一方也可以级联操作。例如,添加了一个腾讯的李四,保存李四的同时也添加腾讯公司的信息

<!-- 
            级联操作:   cascade
                save-update: 级联保存更新
                delete:级联删除
                all:save-update+delete
            级联操作: 简化操作.目的就是为了少些两行代码.
          -->
          <!-- 多的一方: 不能放弃维护关系的.外键字段就在多的一方.  -->
        <many-to-one name="customer" column="lkm_cust_id" class="Customer"  cascade="save-update" >
        </many-to-one>
//3操作
        Customer c = new Customer();
        c.setCust_name("腾讯");

        LinkMan lm1 = new LinkMan();
        lm1.setLkm_name("李四");

        //表达一对多,客户下有多个联系人
        c.getLinkMans().add(lm1);

        //表达对对对,联系人属于哪个客户
        lm1.setCustomer(c);
        //只保存李四,不保存腾讯,但是因为级联或保存腾讯
        session.save(lm1);


下面是执行的SQL语句,添加了一个腾讯的李四,所以在表中添加腾讯customer,linkman李四

先添加腾讯信息
再添加李四信息(包含了外键腾讯,这是linkman自己维护外键)
维护外键(这是customer方维护外键)

inverse 英[ˌɪnˈvɜ:s]
美[ˌɪnˈvɜ:rs]
adj. 相反的; 逆向的; 倒转的;
n. 相反; 倒转; 相反的事物;
vt. 使倒转; 使颠倒;

很明显,最后的维护外键是不需要的,所以在配置文件中配置

Hibernate: 
    insert 
    into
        cst_customer
        (cust_name, cust_source, cust_industry, cust_level, cust_linkman, cust_phone, cust_mobile) 
    values
        (?, ?, ?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        cst_linkman
        (lkm_gender, lkm_name, lkm_phone, lkm_email, lkm_qq, lkm_mobile, lkm_memo, lkm_position, lkm_cust_id) 
    values
        (?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: 
    update
        cst_linkman 
    set
        lkm_cust_id=? 
    where
        lkm_id=?
<!-- inverse:反转,是否反转维护外键的权利
                true:反转,把权利给对方,Customer不维护
                false(默认):不反转,我来维护外键关系,Customer维护

                inverse:性能优化,提高关系维护性能

                在一对多的关系中,因为外键在多的一方,所以多的一方是不可以放弃维护外键的
                你总不能连自己的列都不维护把

                总之,不管怎么搞,都有人来维护外键
                让一的一方放弃维护外键,太高效率
           -->
        <set name="linkMans" cascade="save-update" inverse="true" >
            <key column="lkm_cust_id"></key>
            <one-to-many class="LinkMan"/>
        </set>
//这行代码也可以不用写了
//因为customer已经不维护外键了
//对象中的linkmans集合已经和我没关系了
c.getLinkMans().add(lm1);

如果customer维护外键,不设置级联,然后删除阿里对象,那么会删除从表的阿里的联系人,同时删除阿里customer

如果customer不维护外键,不置级联,然后删除阿里对象,那么会删除从表的阿里的联系人,同时删除阿里customer

多对多

//角色对象
public class Role { 
    private Long role_id;
    private String role_name;
    private String role_memo;
    //表达多对多
    private Set<User> users = new HashSet<User>();
    }
<?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 package="com.hero.pojo" >
    <class name="Role" table="sys_role" >
        <id name="role_id"  >
            <generator class="native"></generator>
        </id>
        <property name="role_name"  ></property>
        <property name="role_memo"  ></property>

    <!-- 使用inverse属性
            true: 放弃维护外键关系
            false(默认值):维护关系

        结论: 将来在开发中,如果遇到多对多关系.一定要选择一方放弃维护关系.
             一般谁来放弃要看业务方向. 例如录入员工时,公司的各个职位都已经订好了。需要为员工指定所属角色,那么就是添加员工,指定角色。
             就是由员工对象来维护关系
             . 角色不需要维护与员工关系.角色放弃维护
         -->        
        <set name="users" table="sys_user_role" inverse="true" >
            <key column="role_id" ></key>
            <many-to-many class="User" column="user_id" ></many-to-many>
        </set>
    </class>
</hibernate-mapping>
public class User {

    private Long user_id;
    private String user_code;
    private String user_name;
    private String user_password;
    private Character user_state;
    //表达多对多
    private Set<Role> roles = new HashSet<Role>();
    }
<?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 package="com.hero.pojo" >
    <class name="User" table="sys_user" >
        <id name="user_id"  >
            <generator class="native"></generator>
        </id>
        <property name="user_code"  ></property>
        <property name="user_name"  ></property>
        <property name="user_password"  ></property>
        <property name="user_state"  ></property>

        <!-- 多对多关系表达 -->
        <!-- 
            name: 集合属性名
            table: 配置中间表名
            key
             |-column:外键,别人引用"我"的外键列名
             class: 我与哪个类是多对多关系
             column:外键.我引用别人的外键列名
         -->
         <!-- cascade级联操作:
                    save-update: 级联保存更新
                    delete:级联删除
                    all:级联保存更新+级联删除
            结论: cascade简化代码书写.该属性使不使用无所谓. 建议要用只用save-update.
                 如果使用delete操作太过危险.尤其在多对多中.不建议使用.
                     -->
        <set name="roles" table="sys_user_role" cascade="save-update" >
            <key column="user_id" ></key>
            <many-to-many class="Role" column="role_id" ></many-to-many>
        </set>

    </class>
</hibernate-mapping>

操作和一对多是一样的

//这个代码出现了报错
//在配置的时候,没有配置inverse属性
//所以默认是都维护外键
//在一对多的时候,双方维护外键
//先建立主表,然后建立从表,从表顺便维护外键,在建立从表的时候写好外键数据
//然后主表维护外键,把对于的从表的外键修改成目标数据


//但是多对多
//双方都会维护中间表
//user表建立插入数据,role表建立插入数据,中间表建立
//user维护主键,让中间表插入数据
//role表维护主键,让中间表插入数据
//这样主键就冲突了
        User user1 = new User();
        User user2 = new User();

        Role role1 = new Role();
        Role role2 = new Role();

        user1.setUser_name("李彦宏");
        user2.setUser_name("麻花藤");

        role1.setRole_name("保安");
        role2.setRole_name("保镖");

        user1.getRoles().add(role1);
        user1.getRoles().add(role2);

        user2.getRoles().add(role1);
        user2.getRoles().add(role2);

        role1.getUsers().add(user1);
        role1.getUsers().add(user2);

        role2.getUsers().add(user1);
        role2.getUsers().add(user2);

        session.save(user1);
        session.save(user2);
        session.save(role1);
        session.save(role2);

        //-------------------------------------------------
        //4提交事务
        tx.commit();

解决办法1:java代码少些一半,不写

role1.getUsers().add(user1);
        role1.getUsers().add(user2);

        role2.getUsers().add(user1);
        role2.getUsers().add(user2);

这样只有一方维护外键了

解决办法2:inverse
写上面的java代码,但是配置中让某一方放弃维护


//为郝强勇新增一个角色
//3操作
        //1> 获得郝强勇用户
        User user = session.get(User.class, 1l);
        //2> 创建公关角色
        Role r = new Role();
        r.setRole_name("男公关");
        //3> 将角色添加到用户中
        user.getRoles().add(r);
        //在user中配置级联,那么就不用下面的保存角色代码了
        //4> 将角色转换为持久化
        //session.save(r);
        //-------------------------------------------------
        //4提交事务
//为郝强勇解除一个角色
//3操作
        //1> 获得郝强勇用户
        User user = session.get(User.class, 1l);
        //2> 获得要操作的角色对象(保洁,保安)
        Role r1 = session.get(Role.class, 1l);
        Role r2 = session.get(Role.class, 2l);
        //3> 将角色从用户的角色集合中移除
        user.getRoles().remove(r1);
        user.getRoles().remove(r2);

        //-------------------------------------------------
        //4提交事务

day4

查询


查询总结

五种查询:
1.oid查询 : get (customer.class,1L) 根据主键
2.对象属性导航查询 :根据customer对象,通过getLinkMan()方法来查询联系人
3:HQL
4:criteria
5:原生态SQL

前两种太简单了,SQL也不用讲

查询HQL

//如果没有重复的类名,可以省略包名
        String hql = "from Customer";

        Query query = session.createQuery(hql);

        List<Customer> list = query.list();

        System.out.println(list);
//查询所有表,没什么卵用,还可能死机
String hql = "from java.lang.Object";

//不可以出现数据库表的字段,必须是类中的属性字段
//根据cust_id排序
String hql = "from Customer order by cust_id asc ";
String hql2 = "from Customer order by cust_id desc ";
//统计查询
        String hql = "select count(*) from Customer ";
        String hql2 = "select avg(cust_id) from Customer ";
        String hql3 = "select sum(cust_id) from Customer ";
        String hql4 = "select max(cust_id) from Customer ";
        String hql5 = "select min(cust_id) from Customer ";
String hql = "select cust_id from Customer ";
查询出的结果是List<string>
String hql = "select cust_id,cust_name from Customer ";
查询出的结果是List<Object[]>
//你不想用List<Object[]>来接受,想用对象customer对象来接受
//这个时候一定要有构造器Customer(cust_id,cust_name){}
//同时还要有空参构造器
String hql = "select new Customer(cust_id,cust_name) from Customer ";

hql多表查询,HQL的语句进行多表查询用的比较少,因为语法比较诡异


//学习HQL语法(不常用) - 多表查询语法
public class Demo2 {
    //回顾-原生SQL
    // 交叉连接-笛卡尔积(避免)
//      select * from A,B 
    // 内连接
//      |-隐式内连接
//          select * from A,B  where b.aid = a.id
//      |-显式内连接
//          select * from A inner join B on b.aid = a.id
    // 外连接
//      |- 左外
//          select * from A left [outer] join B on b.aid = a.id
//      |- 右外
//          select * from A right [outer] join B on b.aid = a.id
//---------------------------------------------------------------------
//HQL的多表查询
        //内连接(迫切)
        //外连接
//          |-左外(迫切)
//          |-右外(迫切)

    @Test
    //HQL 内连接 => 将连接的两端对象分别返回.放到数组中.
    public void fun1(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //----------------------------------------------------
        String hql = " from Customer c inner join c.linkMens ";

        Query query = session.createQuery(hql);

        List<Object[]> list = query.list();

        for(Object[] arr : list){
            System.out.println(Arrays.toString(arr));
        }
        //----------------------------------------------------
        tx.commit();
        session.close();

    }

    @Test
    //HQL 迫切内连接 => 帮我们进行封装.返回值就是一个对象
    public void fun2(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //----------------------------------------------------
        String hql = " from Customer c inner join fetch c.linkMens ";

        Query query = session.createQuery(hql);

        List<Customer> list = query.list();

        System.out.println(list);
        //----------------------------------------------------
        tx.commit();
        session.close();

    }

    @Test
    //HQL 左外连接 => 将连接的两端对象分别返回.放到数组中.
    public void fun3(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //----------------------------------------------------
        String hql = " from Customer c left join c.linkMens ";

        Query query = session.createQuery(hql);

        List<Object[]> list = query.list();

        for(Object[] arr : list){
            System.out.println(Arrays.toString(arr));
        }
        //----------------------------------------------------
        tx.commit();
        session.close();

    }
    @Test
    //HQL 右外连接 => 将连接的两端对象分别返回.放到数组中.
    public void fun4(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //----------------------------------------------------
        String hql = " from Customer c right join c.linkMens ";

        Query query = session.createQuery(hql);

        List<Object[]> list = query.list();

        for(Object[] arr : list){
            System.out.println(Arrays.toString(arr));
        }
        //----------------------------------------------------
        tx.commit();
        session.close();

    }

}

查询criteria

//排序
        Criteria criteria = session.createCriteria(Customer.class);

        //criteria.addOrder(Order.asc(""));
        criteria.addOrder(Order.desc("cust_id"));

        List<Customer> list = criteria.list();

离线查询

查询优化

1.类级别查询(单表查询,查询一个类)
懒加载|延迟加载
get没有策略
load应用类级别的加载策略,默认是懒加载
使用对象的时候session必须是没有被关闭的
建议使用load

//get方法,是执行后立即执行SQL语句
Customer c = session.get(Customer.class,2L);
//get不具有懒加载的功能
//load方法的作用是和get一样的,但是他是延迟加载的
Customer c = session.load(Customer.class,2L);
//在使用到对象的时候才会打印SQL语句
syso(c)
// 是否对类进行延迟加载: 可以通过在class元素上配置lazy属性来控制.
        //lazy:true  加载时,不查询.使用时才查询b(默认是true)
        //lazy:false 加载时立即查询.
//如果在session关闭后才使用对象,就会空指针

2.关联级别查询
集合策略,根据customer取linkman,早customer中配置

//集合级别的关联
Customer c = session.get(Customer.class, 2l);

        Set<LinkMan> linkMens = c.getLinkMens();//关联级别

        System.out.println(linkMens);
<class name="Customer" table="cst_customer" lazy="false" >
        <id name="cust_id"  >
            <generator class="native"></generator>
        </id>
        <property name="cust_name" column="cust_name" ></property>
        <property name="cust_source" column="cust_source" ></property>
        <property name="cust_industry" column="cust_industry" ></property>
        <property name="cust_level" column="cust_level" ></property>
        <property name="cust_linkman" column="cust_linkman" ></property>
        <property name="cust_phone" column="cust_phone" ></property>
        <property name="cust_mobile" column="cust_mobile" ></property>
<!-- 
        lazy属性: 决定是否延迟加载
            true(默认值): 延迟加载,懒加载
            false: 立即加载
            extra: 极其懒惰
        fetch属性: 决定加载策略.使用什么类型的sql语句加载集合数据
            select(默认值): 单表查询加载
            join: 使用多表查询加载集合
            subselect:使用子查询加载集合
     -->
     <!-- batch-size: 抓取集合的数量为3.
            抓取客户的集合时,一次抓取几个客户的联系人集合.
      -->
        <set name="linkMens" lazy="" fetch="" batch-size="3"  >
            <key column="lkm_cust_id" ></key>
            <one-to-many class="LinkMan" />
        </set>
</class>
//默认就是这种方式
//集合级别的关联
    //fetch:select 单表查询
    //lazy:true 使用时才加载集合数据.
    Customer c = session.get(Customer.class, 2l);

        Set<LinkMan> linkMens = c.getLinkMens();//关联级别

        System.out.println(linkMens);
///集合级别的关联
        //fetch:select 单表查询
        //lazy:extra 极其懒惰.与懒加载效果基本一致. 如果只获得集合的size.只查询集合的size(count语句)
Customer c = session.get(Customer.class, 2l);

            Set<LinkMan> linkMens = c.getLinkMens();//关联级别

            System.out.println(linkMens.size());

            System.out.println(linkMens);
//集合级别的关联
        //fetch:join    多表查询,一次性多个表全部查,所以lazy失效
        //lazy:true|false|extra 失效.立即加载
Customer c = session.get(Customer.class, 2l);

            Set<LinkMan> linkMens = c.getLinkMens();//关联级别

            System.out.println(linkMens.size());

            System.out.println(linkMens);
//fetch: subselect 子查询
        //lazy: true 懒加载

String  hql = "from Customer";

            Query query = session.createQuery(hql);

            List<Customer> list = query.list();

            for(Customer c:list){
                System.out.println(c);
                System.out.println(c.getLinkMens().size());
                System.out.println(c.getLinkMens());
            }
//fetch: subselect 子查询
        //lazy: false 立即加载
String  hql = "from Customer";

            Query query = session.createQuery(hql);

            List<Customer> list = query.list();

            for(Customer c:list){
                System.out.println(c);
                System.out.println(c.getLinkMens().size());
                System.out.println(c.getLinkMens());
            }
//fetch: subselect 子查询
        //lazy: extra 极其懒惰
String  hql = "from Customer";

            Query query = session.createQuery(hql);

            List<Customer> list = query.list();

            for(Customer c:list){
                System.out.println(c);
                System.out.println(c.getLinkMens().size());
                System.out.println(c.getLinkMens());
            }

关联属性策略

根据linkman取customer

<class name="LinkMan" table="cst_linkman" >
        <id name="lkm_id"  >
            <generator class="native"></generator>
        </id>
        <property name="lkm_gender"  ></property>
        <property name="lkm_name"  ></property>
        <property name="lkm_phone"  ></property>
        <property name="lkm_email"  ></property>
        <property name="lkm_qq"  ></property>
        <property name="lkm_mobile"  ></property>
        <property name="lkm_memo"  ></property>
        <property name="lkm_position"  ></property>
        <!-- 
        fetch 决定加载的sql语句
            select: 使用单表查询
            join : 多表查询
        lazy  决定加载时机
            false: 立即加载
            proxy: 由customer的类级别加载策略决定.
         -->
    <many-to-one name="customer" column="lkm_cust_id" 
    class="Customer" fetch="join" lazy="proxy"  >
        </many-to-one>
    </class>

结论
为了提高效率,fetch的值选择select(也就是默认值)
lazy 的取值选择true(也就是默认值)
总之,不用配置

所以这里有lazy的no-session问题
如果session关闭后,才使用对象,懒加载的对象就空指针了
web层使用对象,但是dao层的session已经关闭了
解决办法:扩大session作用范围,原来是包裹service的范围,现在扩大到包裹三层
使用过滤器,包裹web层

批量抓取()

<!-- batch-size: 抓取集合的数量为3.
            抓取客户的集合时,一次抓取几个客户的联系人集合.
      -->
        <set name="linkMens" lazy="" fetch="" batch-size="3"  >
            <key column="lkm_cust_id" ></key>
            <one-to-many class="LinkMan" />
        </set>
String  hql = "from Customer";

            Query query = session.createQuery(hql);

            List<Customer> list = query.list();
            //查询到所有的customer,遍历出来
            //每个customer对象中的集合,会一条条发送SQL(where 外键=“”)
            //配置了batch-size后,就是一次性3条SQL语句发送过去
            for(Customer c:list){
                System.out.println(c);
                System.out.println(c.getLinkMens().size());
                System.out.println(c.getLinkMens());
            }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值