• 框架学习之Hibernate 第十一节 Hibernate知识补充


    1.配置文件 hibernate.cfg.xml

    参见官方文档

    2.映射文件 hbm.xml

    参见官方文档

    重点:主键生成方式

    native:根据使用的数据库来确定id的生成方式

    如果是插入操作的话,在插入之前就会对数据库进行一次访问来生成下一个id,然后才插入,也就是插入了之后才知道id

    hilo:高低位方式,一部分是数据库生成的,另一部分是程序生成的,可以保证是不会重复的,这种方式和上面的不同之处在于它不是插入了之后才知道id的[我自己也很糊涂]

    采用这种方式数据库中会生成一个表 hibernate-unique-key(默认情况),表中有一个字段 next-hi,意思就是下一个高位

    如果是native类型获取主键,对mysql来说是自增长方式的,只有在插入数据库后才能拿到主键;而用hilo方式获取主键,这种方式不用马上插入数据也可以拿到主键

    hilo

    foreign:外键引用,通常要和one-to-one标签一起使用

    在一对一映射中要指定引用的是哪个表的哪个字段

            <id name="id">
                <generator class="foreign">
                    <param name="property">person</param>
                </generator>
            </id>

    uuid:UUID的生成方式生成id时不用访问一次数据库,它生成的id肯定是不同的,并且是string

    如果插入比较多的话,可以考虑这种方式,但是查询效率会降低,因为比较的是string

    3.Hibernate的类型

    (1)<property name=“name” type=“java.lang.String”/>

    type可以是hibernate、java类型或者你自己的类型(需要实现hibernate的一个接口)。

    (2)基本类型一般不需要在映射文件(hbm.xml)中说明,只有在一个JAVA类型和多个数据库数据类型相对应时并且你想要的和hibernate缺省映射不一致时,需要在映射文件中指明类型(如:java.util.Date,数据库DATE,TIME,DATATIME,TIMESTAMP,hibernate缺省会把java.util.Date映射成DATATIME型,而如果你想映射成TIME,则你必须在映射文件中指定类型)。

    默认的情况下,如果 java.util.Date   ->  datetime(数据库中)

    如果只想保存日期,type设置成date

    如果只想保存时间,type设置成time

    如果两个都想保存,type设置成timestamp

    (3)自定义类型,只要实现了接口就行

    文档:

    数据类型的对应关系:

    内置的 basic mapping types 可以大致地分类为:
    integer, long, short, float, double, character, byte, boolean, yes_no, true_false
    这些类型都对应 Java 的原始类型或者其封装类,来符合(特定厂商的)SQL 字段类型。boolean, yes_no 和 true_false 都是 Java 中 boolean 或者 java.lang.Boolean 的另外说法。

    string
    从 java.lang.String 到 VARCHAR(或者 Oracle 的 VARCHAR2)的映射。

    date, time, timestamp
    从 java.util.Date 和其子类到 SQL 类型 DATE,TIME 和 TIMESTAMP(或等价类型)的映射。

    calendar, calendar_date
    从 java.util.Calendar 到 SQL 类型 TIMESTAMP 和 DATE(或等价类型)的映射。

    big_decimal, big_integer
    从 java.math.BigDecimal 和 java.math.BigInteger 到 NUMERIC(或者 Oracle 的 NUMBER类型)的映射。

    locale, timezone, currency
    从 java.util.Locale,java.util.TimeZone 和 java.util.Currency 到 VARCHAR(或者 Oracle 的VARCHAR2 类型)的映射。Locale 和 Currency 的实例被映射为它们的 ISO 代码。TimeZone 的实例被影射为它的 ID。

    class
    从 java.lang.Class 到 VARCHAR(或者 Oracle 的 VARCHAR2 类型)的映射。Class 被映射为它的全限定名。

    binary
    把字节数组(byte arrays)映射为对应的 SQL 二进制类型。

    text
    把长 Java 字符串映射为 SQL 的 CLOB 或者 TEXT 类型。

    serializable
    把可序列化的 Java 类型映射到对应的 SQL 二进制类型。你也可以为一个并非默认为基本类型的可序列化 Java 类或者接口指定 Hibernate 类型 serializable。

    clob, blob
    JDBC 类  java.sql.Clob 和  java.sql.Blob的映射。某些程序可能不适合使用这个类型,因为blob 和 clob 对象可能在一个事务之外是无法重用的。(而且, 驱动程序对这种类型的支持充满着补丁和前后矛盾。)

    imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date, imm_serializable, imm_binary
    一般来说,映射类型被假定为是可变的 Java 类型,只有对不可变 Java 类型,Hibernate 会采取特定的优化措施,应用程序会把这些对象作为不可变对象处理。比如,你不应该对作为 imm_timestamp 映射的 Date 执行 Date.setTime()。要改变属性的值,并且保存这一改变,应用程序必须对这一属性重新设置一个新的(不一样的)对象。

    实体及其集合的唯一标识可以是除了 binary、 blob 和 clob 之外的任何基础类型。

    4.Session和SessionFactory

    Session是非线程安全的,生命周期较短,代表一个和数据库的连接,在B/S系统中一般不会超过一个请求;

    内部维护一级缓存和数据库连接,如果session长时间打开,会长时间占用内存和数据库连接。

    SessionFactory是线程安全的,一个数据库对应一个SessionFactory,生命周期长,一般在整个系统生命周期内有效;

    SessionFactory保存着和数据库连接的相关信息(user,password,url)和映射信息,以及Hibernate运行时要用到的一些信息。

    线程安全 意思就是多个地方进行访问同一个数据时,数据是同步的,不会乱的,因为正在访问的那个数据会被锁死,其他人没有办法访问

    还有一种线程安全是 static 的,静态的,不会改变的,以及只读的属性,它们的值是不能够被修改的(final)

    5. flush时将一级缓存与数据库同步,这个方法Hibernate自己会在相应的时间调用,最好不要自己调用,因为这个方法耗时耗资源

    一般在查询之前或者提交之前执行

    测试代码:[注:Department是 native的id生成器,而Employee是hilo的形式]

    /**
    * @Author:胡家威  
    * @CreateTime:2011-8-15 下午11:18:42
    * @Description:
    */
    
    package com.yinger.main;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import org.hibernate.Session;
    import org.hibernate.Transaction;
    
    import com.yinger.domain.Department;
    import com.yinger.domain.Employee;
    import com.yinger.util.HibernateUtils;
    
    public class One2Many {
    
        public static void main(String[] args) {
            add();
        }
    
        private static void add() {
            Department depart = new Department();
            depart.setName("depart name");
            
            Employee emp1 = new Employee();
            emp1.setName("emp1 name");
            
            Employee emp2 = new Employee();
            emp2.setName("emp2 name");
            
            Set<Employee> emps = new HashSet<Employee>();
            emps.add(emp1);
            emps.add(emp2);
            depart.setEmps(emps);
            
            Session s = null;
            Transaction tr = null;
            try{
                s = HibernateUtils.getSession();
                tr = s.beginTransaction();
                s.save(depart);
                s.save(emp1);
                s.save(emp2);
                s.flush();
                System.out.println("--------------------");
                tr.commit();
            }catch(Exception e){
                if(tr!=null)
                    tr.rollback();
            }finally{
                if(s!=null)
                    s.close();
            }
        }
    
    }

    测试结果: 两条Employee的insert语句是在横线之上的

    log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
    log4j:WARN Please initialize the log4j system properly.
    Hibernate: insert into Department (name) values (?)
    Hibernate: insert into Employee (name, id) values (?, ?)
    Hibernate: insert into Employee (name, id) values (?, ?)
    Hibernate: update Employee set depart_id=? where id=?
    Hibernate: update Employee set depart_id=? where id=?
    --------------------

    注释掉 s.flush() 方法之后,insert语句在横线的下面

    log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
    log4j:WARN Please initialize the log4j system properly.
    Hibernate: insert into Department (name) values (?)
    --------------------
    Hibernate: insert into Employee (name, id) values (?, ?)
    Hibernate: insert into Employee (name, id) values (?, ?)
    Hibernate: update Employee set depart_id=? where id=?
    Hibernate: update Employee set depart_id=? where id=?

    前者调用了flush方法,使得一级缓存和数据库进行了同步,这样就可以立即插入Employee对象了

    后者则不会,Hibernate默认情况下都会对数据库操作进行优化,一般都会在最后的时刻进行数据库操作,也就是批量的进行数据库的CURD

    这样可以提高数据库操作的效率

    6.大批量处理

    大量操作数据时可能造成内存溢出,解决办法如下:

    清除session中的数据

    for(int i=0;i<100000;i++)      session.save(obj);

    for(int i=0;i<100000;i++){

             session.save(obj);

             if(i% 50 == 0){

                       session.flush(); // 一般要在clear之前进行一次flush操作,因为并不知道什么时候修改了数据,所以要在清除缓存之前和数据库进行一次同步

                       session.clear();

             }

    }

    ②用StatelessSession接口它不和一级缓存、二级缓存交互,也不触发任何事件、监听器、拦截器,通过该接口的操作会立刻发送给数据库,与JDBC的功能一样。

    StatelessSession s = sessionFactory.openStatelessSession();该接口的方法与Session类似。

    Query.executeUpdate()执行批量更新,会清除相关联的类二级缓存(sessionFactory.evict(class))

    缺点:可能会造成级联和乐观锁定问题,因为二级缓存中的数据已被清空了

    测试代码:

        public static void main(String[] args) {
            People p = addPeople();
            System.out.println("---------");
            excuteUpdateTest();
        }
        
        public static void excuteUpdateTest() {
            // hibernate 3.0 之前,批量更新只能是把数据一个个取出来然后更新
            Session session = HibernateUtils.getSession();
            Transaction tx = session.beginTransaction();
            SQLQuery query = session.createSQLQuery("select {p.*} from people p").addEntity("p", People.class);
            List<People> list = query.list();
            for(People people:list) {
                people.getName().setFirstName("new firstName");
            }
            // hibernate 3.0 之后,有了 excuteUpdate 方法 update Customer set name = :newName
            Query query2 = session.createQuery("update People set first_name=:newName");
            query2.setString("newName", "new name");
            query2.executeUpdate();
            tx.commit();
        }

    测试结果:

    log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
    log4j:WARN Please initialize the log4j system properly.
    Hibernate: insert into People (version, first_name, last_name, id) values (?, ?, ?, ?)
    ---------
    Hibernate: select p.id as id0_, p.version as version8_0_, p.first_name as first3_8_0_, p.last_name as last4_8_0_ from people p
    Hibernate: update People set version=?, first_name=?, last_name=? where id=? and version=?
    Hibernate: update People set first_name=?

    数据库中的数据:

    id  version  first_name  last_name

    1    1    new name    lastName

    7.拦截器和事件监听

    拦截器与事件都是hibernate的扩展机制(类似过滤器),Interceptor接口是老的实现机制,现在改成事件监听机制;

    他们都是hibernate的回调接口,hibernate在save,delete,update…等会回调这些类。

    测试代码:Hibernate 3.0 之后将拦截器改成了事件监听!当然,拦截器还是有的

    [ 这部分内容我测试失败,估计是我的Hibernate的版本的问题,实现的方法的返回值都是不同的,以下摘自百度]

    以下摘自:http://hi.baidu.com/sonmeika/blog/item/b83d1c12aa687449f819b8b2.html   传智播客hibernate视频教程(十五):其他问题汇总

    // 示例:
     // 在hibernate.cfg.xml中添加:
    <hibernate-configuration>
     <session-factory name="sessionFactory">
    ......
      <!-- 映射文件存放位置 -->
      <mapping resource="cn/itcast/hibernate/domain/User.hbm.xml"/>
      <mapping resource="cn/itcast/hibernate/domain/Department.hbm.xml"/>
      <mapping resource="cn/itcast/hibernate/domain/Employee.hbm.xml"/>
      <mapping resource="cn/itcast/hibernate/domain/Person.hbm.xml"/>
      <mapping resource="cn/itcast/hibernate/domain/IdCard.hbm.xml"/>
      <mapping resource="cn/itcast/hibernate/domain/Teacher.hbm.xml"/>
      <mapping resource="cn/itcast/hibernate/domain/Student.hbm.xml"/>
      <!-- 定义监听器 -->
      <event type="save">
       <listener class="cn.itcast.hibernate.SaveListener"/>
      </event>
     </session-factory>
    </hibernate-configuration>
    
    SaveListener.java:
    package cn.itcast.hibernate;
    import org.hibernate.HibernateException;
    import org.hibernate.event.SaveOrUpdateEvent;
    import org.hibernate.event.SaveOrUpdateEventListener;
    import cn.itcast.hibernate.domain.User;
    public class SaveListener implements SaveOrUpdateEventListener {
     private static final long serialVersionUID = -5774832715365370704L;
     @Override
     public void onSaveOrUpdate(SaveOrUpdateEvent event)
       throws HibernateException {
      if (event.getObject() instanceof cn.itcast.hibernate.domain.User) {
       User user = (User) event.getObject();
       System.out.println("--"+user.getId()); //如果有id值,就证明已经保存了
       System.out.println("--"+user.getName());
      }
     }
    }
    这样在保存user时都会先经过onSaveOrUpdate。例如:
      User user = new User();
      user.setBirthday(new Date());
      user.setName("name");
      System.out.println("before save");
      addUser(user);
      System.out.println("after save");
    输出如下:
    before save
    --0
    --name
    after save
    在输出内容中看不到insert语句了,并且库里也没有添加记录。这是因为我们自定义的监听器覆盖了原有的监听器,hibernate认为已经定义了更好的监听器,就不执行默认的了。如果要继续执行默认的,则需要在hibernate.cfg.xml中增加如下:
      <!-- 定义监听器 -->
      <event type="save">
       <listener class="cn.itcast.hibernate.SaveListener"/>
       <listener class="org.hibernate.event.def.DefaultSaveEventListener"/><!-- 注意:是DefaultSaveEventListener,而不是DefaultSaveOrUpdateEventListener -->
       </event>
    监听器的调用顺序与此处的顺序有关,先调用SaveListener,后调用DefaultSaveEventListener。
    修改后的输出如下:
    before save
    --0  // ID是0,说明此时还没有保存
    Hibernate: select hibernate_sequence.nextval from dual
    Hibernate: insert into Persons (name, birthday, id) values (?, ?, ?)
    after save
    调整监听器的顺序如下:
      <event type="save">
       <listener class="org.hibernate.event.def.DefaultSaveEventListener"/><!-- 注意:是DefaultSaveEventListener,而不是DefaultSaveOrUpdateEventListener -->
       <listener class="cn.itcast.hibernate.SaveListener"/>
       </event>
    输出如下:
    before save
    Hibernate: select hibernate_sequence.nextval from dual
    --1  // ID是1,说明对象已保存
    Hibernate: insert into Persons (name, birthday, id) values (?, ?, ?)
    after save

    8. SQL查询 和 命名查询

    s.createSQLQuery:建议不要使用,因为兼容性不好,对于数据库的移植会造成影响

    s.getNamedQuery:写在映射文件中的HQL,统一的管理,便于以后使用的方便

    命名的sql放置在映射文件中,放置的位置不同(class内或者class外),传递的字符串的参数是不同的

    测试代码:

    在People的映射文件中添加:放在class外面

    <query name="getFirstNameById">
            <![CDATA[from People where id=:id]]>
    </query>

    测试类中的方法:

    package com.yinger.main;
    
    import java.util.List;
    
    import org.hibernate.Query;
    import org.hibernate.SQLQuery;
    import org.hibernate.Session;
    import org.hibernate.Transaction;
    
    import com.yinger.domain.Name;
    import com.yinger.domain.People;
    import com.yinger.util.HibernateUtils;
    
    public class QueryTest {
    
        public static void main(String[] args) {
            People p = addPeople();
            System.out.println("---------");
            sqlTest();
            System.out.println("---------");
            namedSqlTest(p.getId());
        }
    
        public static void sqlTest() {
            Session session = HibernateUtils.getSession();
            SQLQuery query = session.createSQLQuery("select {p.*} from people p").addEntity("p", People.class); // 注意这里是SQLQuery,不是Query
            List<People> list = query.list();
            for(People people:list) {
                System.out.println(people.getName().getFirstName());
            }
        }
        
        public static void namedSqlTest(int id) {
            Session session = HibernateUtils.getSession();
            Query query = session.getNamedQuery("getFirstNameById");
            query.setInteger("id", id);
            List<People> list = query.list();
            for(People people:list) {
                System.out.println(people.getName().getFirstName());
            }
        }
    
        public static People addPeople() {
            People p = new People();
            Name n = new Name();
            n.setFirstName("firstName");
            n.setLastName("lastName");
            p.setName(n);
            HibernateUtils.add(p);
            return p;
        }
    
        private static void testTransaction(int id) {
            Session s1 = HibernateUtils.getSession();
            Transaction tx1 = s1.beginTransaction();//一个事务开启了
            People p1 = (People)s1.get(People.class, id);
            
            Session s2 = HibernateUtils.getSession();;
            Transaction tx2 = s2.beginTransaction();//又开启了另一个事务,并且操作的是同一条数据
            People p2 = (People)s2.get(People.class, id);
            
            p2.getName().setFirstName("firstName 2");
            p1.getName().setFirstName("firstName 1");
            
            tx2.commit();
            tx1.commit();
            s1.close();
            s2.close();
        }
    
    }
    

    测试结果:

    log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
    log4j:WARN Please initialize the log4j system properly.
    Hibernate: insert into People (version, first_name, last_name) values (?, ?, ?)
    ---------
    Hibernate: select p.id as id0_, p.version as version8_0_, p.first_name as first3_8_0_, p.last_name as last4_8_0_ from people p
    firstName
    ---------
    Hibernate: select people0_.id as id, people0_.version as version8_, people0_.first_name as first3_8_, people0_.last_name as last4_8_ from People people0_ where (people0_.id=?)
    firstName

    9. 不合适使用 Hibernate的场景

    ①不适合OLAP(On-Line Analytical Processing 联机分析处理),以查询分析数据为主的系统;适合OLTP(on-line transaction processing 联机事务处理)。

    联机分析处理 系统总是进行查询统计,没有很好的对象和关系模型,不适合使用 Hibernate

    ②对于些关系模型设计不合理的老系统,也不能发挥hibernate优势。

    ③数据量巨大,性能要求苛刻的系统,hibernate也很难达到要求, 批量操作数据的效率也不高。

    10. 与 JPA 集成 (annotation方式)

    ①需要添加的包ejb3-persistence.jar, hibernate-entitymanager.jar, hibernate-annotations.jar, hibernate-commons-annotations.jar, jboss-archive-browsing.jar, javassist.jar

    ②配置文件%CLASSPATH%/META-INF/persistence.xml

    ③JAVA代码:

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(name);
        //(Name:在persistence.xml中指定。)
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        Tx.begin();
        Em.persist(entity);//remove,merge,find
        Tx.commit();
        Em.close();
        Emf.close();

    相关的PPT:

    幻灯片44

    幻灯片45

    幻灯片47

    幻灯片48

    幻灯片49

  • 相关阅读:
    写了一个html5音乐播放器
    跨站脚本攻击之反射型XSS漏洞
    org.hibernate.ObjectNotFoundException异常一则
    addEventListener第二个参数的handleEvent
    IE的fireEvent方法
    DexMongoDB索引优化工具
    javascript 位运算符
    javascript contains方法来判断元素包含关系
    将uglifyjs添加到鼠标右键菜单
    过滤XSS(跨站脚本攻击)的函数和防止svn版本库被浏览
  • 原文地址:https://www.cnblogs.com/yinger/p/2145886.html
Copyright © 2020-2023  润新知