框架学习之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: 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: 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();
         query = session.createSQLQuery("select  from people p").
        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 =: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"/>
  <!-- 定义监听器 -->
  < 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  implements  {
 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"/>
   
   </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();
         query = session.createSQLQuery("select {p.*} from people p").addEntity("p", People.class); // 
        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

转载于:https://www.cnblogs.com/yinger/archive/2011/08/19/2145886.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值