SSH之Hibernate

Hibernate较另一个持久层框架MyBatis。Hibernate倡导地入侵式的设计,完全采用普通的Java对象(POJO类),不要求PO继承某个超类或实现Hibernate的某个接口
ORM
对象/关系数据库映射,ORM框架的特征:完成面向对象的编程语言到关系数据库的映射,ORM框架是应用程序和数据库的桥梁
ORM工具提供了持久化类和数据表之间的映射关系,通过这种映射关系的过度,程序员可以很方便地通过持久化类实现对数据表的操作。
基本映射方式
数据表映射类:持久化类被映射到一个数据表,程序使用这个持久化类来创建实例、修改属性、删除实例时,系统自动回转换为对这个表进行CRUD操作
数据表——Model类
数据表的行映射对象:持久化类会生成很多实例,每个实例就对应数据表中的一行记录。
记录一 ——实例1
记录二 ——实例2
记录三 ——实例3
数据表的列映射对象的属性:
列1 列2 列3
l l l
l l l
属性1 属性2 属性3
流行的ORM框架:
JPA
MyBatis
TopLink

package com.hibernate.pojo;
@Entity
// 声明该类是一个Hibernate的持久化类
// 指定该类映射的表
public class News {
@Id
// 用于指定该类的标识属性(唯一标识该对象的属性,标识属性通常映射到数据表的主键列)
@GeneratedValue(strategy = GenerationType.IDENTITY)
// 用于指定主键生成策略,其中strategy属性指定了主键生成策略为IDENTITY策略,也就是采用自动增长的主键生成策略
private Integer id;
private String title;
private String content;
//getter、setter
}
Hibernate的配置文件既可以使用.properties属性文件,也可以使用XML文件配置

Hibernat配置文件的默认文件名为hibernate.cfg.xml,当程序调用Configuration对象的configure()方法时,Hibernate将自动加载盖文建
Hibernate不推荐用DriverManger来连接数据库,而是推荐使用数据原来管理数据库连接,这样能保证最好的性能.Hibernate推荐采用C3P0数据源


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">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<!-- 指定连接数据库所用的驱动 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 指定连接数据库的url,其中hibernate是本应用连接是数据库名 -->
<property name="connection.url">jdbc:mysql://localhost:3306/hibernate</property>
<!-- 指定连接数据库的用户名 -->
<property name="connection.username">root</property>
<!-- 指定连接数据库的密码 -->
<property name="connection.password">root</property>
<!-- 指定连接池里最大的连接数 -->
<property name="hibernate.c3p0.max_size">20</property>
<!-- 指定连接池里最小的连接数 -->
<property name="hibernate.c3p0.min_size">1</property>
<!-- 指定连接池里连接的超时时长 -->
<property name="hibernate.c3p0.timeout">5000</property>
<!-- 指定连接池里最大缓存多少个Statement对象 -->
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<property name="hibernate.c3p0.validate">true</property>
<!-- 指定数据库方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
(不能自动建表的原因是这里没有弄正确)
<!-- 根据需要自动创建数据表 -->
<property name="hbm2dd1.auto">update</property>
<!-- 显示Hibernate持久化操作所生成的SQL -->
<property name="show_sql">true</property>
<!-- 将SQL脚本进行格式化后再输出 -->
<property name="hibernate.format_sql">true</property>
<!-- 罗列所有持久化类的类名 -->
(所有需要持久化的类都必须在这罗列出来否则就会报 org.hibernate.MappingException: Unknown entity 这样的错误 )
<mapping class="com.hibernate.pojo.News"/>
</session-factory>
</hibernate-configuration>

public class NewsManger {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration()
// 不带参数的configure()默认加载hibernate.cfg.xml文件
// 如果传abc.xml作为参数,则不在加载hibernate.cfg.xml,改为加载abc.xml
.configure();
System.out.println("-----加载成功---");
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(conf .getProperties()).build();
// 以configuration实例创建SessionFactory实例
SessionFactory sFactory = conf.buildSessionFactory(serviceRegistry);
// 创建Session
Session session = sFactory.openSession();
// 开始事务
Transaction transaction = session.beginTransaction();
// 创建消息对象
News news = new News();
// 设置消息标题和消息内容
news.setTitle("标题");
news.setContent("消息内容," + "http://www.baidu.com");
// 保存消息
session.save(news);
// 提交事务
transaction.commit();
// 关闭Session
session.close();
sFactory.close();
}
注意事项:数据库、表、表单内容要先创建好,并且id要设置为自增长,否则要设置id值

SessionFactory:Hibernate关键对象,是单个数据库映射关系经过编译后的内存镜像,也是线程安全的。是生成Session的工厂,本身需要依赖ConnectionProvider。该对象可以再进程或者集群的级别上,为事务之间可以重用的数据提供可选的二级缓存
Session:是应用程序与持久存储层之间交互操作的一个单线程对象,特色Hibernate持久化操作的关键对象,所有的持久化对象必须在Session管理下才可以进行持久化操作,此对象生成存起很短。底层封装了JDBC连接,也是Transacation的工厂,Session持有必选的一级缓存,在显示执行flush之前,所有持久化操作都在缓存中的Session的Session处。
持久化对象(Persistent Object):系统创建的POJO实例,一旦与特定的Session关联,并对象数据表的指定记录,该对象就处于持久化状态,这一系列对象都被成为持久化对象。在程序中对持久化对象执行的修改,都将自动被转换为对持久层的修改。持久化对象完全可以是普通的JavaBeans/POJO,唯一的区别是他们正与一个Session关联
瞬态对象和脱管对象:系统通过New关键字创建的Java实例,没有与Session相关联,此时处于瞬态。瞬态实例可能是在被应用程序实例化后尚未进行持久化的对象。如果一个曾经持久化过的实例没如果Session被关闭则转化为托管状态
事务(Transaction):代表一次原子操作,它具有数据库事务的概念。Hibernate事务是对底层具体的JDBC、JTA以及CORBA事务的抽象。在某些情况下,一个Session之内可能包含多个Transaction对象,虽然事务操作是可选的,蛋所有持久化操作都应该在事务管理下进行,即使是只读操作
连接提供者(ConnectionProvider):JDBC的连接工厂没通过抽象将应用程序与底层的DataSource或者DriverManager隔离开。这个对象无需应用程序直接访问,尽在应用程序需要扩展时使用
事务工厂(Transcation):生成Transaction对象实例的工厂,该对象也无需用用程序直接访问,他负责对底层具体的事务进行封装,将底层具体的事务抽象成Hibernate事务

Hibernate持久化对象的状态演化图
备注:当Session调用带有*的方法时,所有与该Session关联的实例都将受到影响4

1.持久化实体:
Serializable save(Object obj):将obj对象变为持久化状态,该对象的属性将被保存到数据库
void persist(Object obj) :将obj对象转化为持久化状态,该对象的属性将被保存到数据库
Serializable save(Object obj,object pk):将obj对象保存到数据库,保存到数据库时,指定主键值
void persist(Object obj,Object obk):将obj对象保存到数据库,保存到数据库时,指定主键值
把一个瞬态实体变成持久化状态时,Hibernate会在底层对应的生成一台insert语句,这条语句的负责把该实体对应的数据记录插入到数据表
2.根据主键加载持久化实体
程序可以通过load()来加载一个持久化实体
News n=session.load(News.class,pk);//pk代表需要加载的持久化实例的标识属性
get方法类似
load()与get()区别
load()具有延迟加载的功能,load()方法不会立即访问数据库,当试图加载的记录不存在时,load()方法可能返回一个未初始化的代理对象,get()立即访问数据库,当记录不存在时,将直接返回Null
3.更新脱管实体
当程序修改托管对象的状态后,程序应该显示的使用新的Session对象来保存这些修改,Hibernate提供了update()、merge()和updateOrSave()方法来保存这些改变

Hibernate映射
@Entity :被该注释修饰的POJO就是一个实体。
@Table :该注释指定持久化类所映射的表
catalog 设置将持久化类映射表放入指定的catalog中
indexs 设置索引 是一个@Index注解数组
columnList 设置对那些建立索引,可指定多个数据列的列名
name 索引名字
unique 设置索引是否具有唯一性,boolean
name 持久化类映射的表的表名(不设置表名则与持久化的类的类名相同)
schema 将持久化类映射的表放入指定的schema中
uniqueConstraints 持久化类所映射的表的唯一约束,值是一个@UniqueConstraint注解数组
columNames 字符串数组,每个字符串元素代表一个数据列
@Proxy :指定一个接口,具有延迟加载时作为代理使用,也可以指定类名
@DynamicInsert: 指定用于插入记录的Insert语句是否在运行时生成,并且只插入非空字段,默认是false
@DynamicUpdate :指定用于更新记录的update语句是否在裕兴时动态生成,并且只更新改变过的字段,默认是false
打开后可以指定一下乐观的锁定策略
->OptimisticLockType.VERSION:检查version/timestamp字段( ☆建议,对性能来说是最好的选择,也是唯一能后处理在Session外进行托管操作的策略)
@SelectBeforeUpdate:指定Hibernate在update某个持久化对象之前是否需要先进行一次select。若为true则Hibernate可以保证只有当持久化对象的状态被修改过时,才会使用update来保存其状态。
@PolymorphismType:当采用TABLE_PER_CLASS继承映射策略时,该注解用于指定是否需要采用隐式多态查询。默认value值为PolymorphismType.IMPLICIT,即支持隐式多态查询
@Where:该注解的clause属性可以指定一个附加的SQL语句过滤条件,如果加入,啧不管采用Load(),get()还是其他的查询方法,只要试图加载该持久化类的对象时,该where条件就会声效,也就是说,只有符合该where条件的记录才会被拦截
@BatchSize:当Hibernate抓取集合属性或延迟加载的实体时,该注解的size属性指定每批抓取的实例数
@OptimisticLocking:该注解的type属性指定乐观锁定策略,Hibernate支持OptimisticLockType.ALL、OptimisticLockType.DIRTY、OpmisticLockType.NONE、OpmisticLockType.VERSION这4个枚举值,默认值是OptimisticLockType.VERSION
@Check:可通过constrains指定一个SQL表达式,用于该持久化类所对应的表指定一个Check约束
@Subselect:该注解用于映射不可变的、只读实体。(将数据库的子查询映射成Hibernate持久化对象,当需要使用视图来代替数据表时,比较有用)

映射属性
被@Entity修饰的持久化类的所有属性都会被映射到底层数据表,为指定某个属性所映射的数据列的详细信息,可以在实体类中使用@Column修饰该属性
columnDefinition SQL字符串,指定创建该数据列的SQL语句
Insertabel 指定该列是否包含在Hibernate声场的insert语句的列表中,默认为true
length 指定该列所能保存的数据的最大长度默认255
name 指定列名,默认与@Column修饰的成员变量的名相同
table 指定该列所属的表名,当需要用多个表来保存一个实体时,需要指定该属性
unique 是否具有唯一约束
@Formula:该注解的value属性可指定一个SQL表达式。
@Generated:设置该属性映射的数据列的值是否由数据库生成,该注解的value属性可以接受GenerationTime.NEVER(不由数据库生成),GenerationTime.INSERT(该属性值在执行insert语句时生成,但不会在执行update语句时重新生成)和GenerationTime.ALWAYS该属性值在执行insert和unpdate语句时都会被重新生成)这三个值其中之一
@Formula的value属性允许对象属性包含表达式,包括运用sum、average、max函数求值的结果
value="(select avg(p,price) from Product p)"/>
或者是一个表的查询结果
value="select cur.name from currency cur where cur.id=currencyID"/>
*注意点
value="(sql)"英文括号不能少
value="()"括号里面是SQL表达式,SQL表达式中的列名与表名都应该和数据库对应,而不是和持久化对象的属性对应
如果需要在@Formula的value属性中使用参数,直接使用where cur.id=currencyID形式,其中currencyID就是参数,当前持久化对象的currencyID属性将作为参数传入
@Formula("(select concat(nt.title,nt.content)"+ "from news_table nt where nt.id= id)")
private String fullContent;

News news2 = (News) session.get(News.class, 1);
System.out.println(news2.getFullContent());
输出的是计算结果,且没有为fullContent属性创建对应的数据列
@Transient修饰不想吃就保存的属性
@Transient
private String content;
@Enumerated修饰枚举类型的属性
@Enumerated(EnumType.ORDINAL)
@Column(name="happen_season")
private Season happenSeason;(Season是自定义类 public enum Season{****})
@Enumerated(EnumType.ORDINAL)修饰了枚举类型的属性,底层数据库会保存数值序号来代表枚举值
使用@Lob@Baisc修饰大数据类型的属性
@Lob
@Basic(fetch=FetchType.LAZY)
private byte[] pic;

File file = new File("leimu.jpg");
byte[] content = new byte[(int) file.length()];
new FileInputStream(file).read(content);
picture.setPic(content);
(@Basic:Hibernate加载Picture对象时并不立即加载他的pic属性,而是只加载一个“虚拟”的代理,等到程序正正需要pic属性时才从底层数据表中加载数据(代理模式))
@Basic
->fetch 指定是否延迟加载该属性,FetchType.EAGER(立即加载)FetchType.LAZY(延迟加载)
->optional:指定该属性映射的数据列是否允许使用Null
@Temporal修饰日期类型的属性
@Temporal(TemporalType.DATE)
private Date birth;

映射主键
@GeneratedValue
->strategy
->GenerationType.AUTO:Hibernate自动选择最适合底层数据库的主键生成策略
->GenerationType.IDENTITY:对于MySQL,SQL Server这样是数据库,选择自增长的主键生成策略
->GenerationType.SEQUENCE:对于Oracle这样的数据库,选择使用基于Sequence的主键生成策略,应该与@SequenceGenerator一起使用
->GenerationType.TABLE :使用辅助表来生成主键,应与@TableGenerator一起使用
->generator:当使用GenerationType.SEQUENCE,GenerationType.TABLE主键生成策略时,该属性引用@SequenceGenerator,@TableGenerator所定义的生成器的名称

使用Hibernate的主键生成策略
@GenericGenerator
->name:必需属性,设置主键生成器的名称,可以被@GeneratedValue的generator属性引用
->strategy:必需属性,设置该主键生成器的主键生成策略
->increment :long,short,int类型主键生成的唯一标识
->identity:在DB2、MySQL、SQLServer等提供identity(自增长)主键支持的数据表中适用
->sequence:在DB2,Oracle等提供Sequence支持的数据表中适用
->uuid:用一个128位的UUID算法生成的字符串类型的标识符,这在一个网络中是唯一的(IP地址也作为算法的数据源)。UUID被编码为一个32位十六进制的字符串
->guid:在SQL Server和MySQL中适用数据库生成的GUID字符串
->native:根据底层数据库的能力选择ident、sequence或者hilo中的一个
->assigned:让程序在save()之前为对象分配一个标识符。
->select:通过数据库触发器选择某个唯一主键的行,并返回其主键值组颇为标识属性值
->foreign:表明直接使用另一个关联的对象的标识属性值。

映射集合属性
@ElementCollection
->fetch :指定该实体对集合属性的抓取策略,FetchType.EAGER(立即抓取)和FetchType.;LAZY(延迟抓取)默认延迟
->targetClass:指定集合属性中集合元素的类型
Hibernate使用标准的@CollectionTable注解映射保存集合属性的表
@JoinColumn注解专门用于定义外键列
@Id
@Column(name = "picture_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
@Lob
private byte[] pic;
@ElementCollection(targetClass = String.class)
@CollectionTable(name = "school_inf", joinColumns = @JoinColumn(name = "picture_id", nullable = false))
@Column(name = "school_name")
@OrderColumn(name = "list_order")
private List<String> schoolsList = new ArrayList<String>();

picture.getSchoolsList().add("第1个");
picture.getSchoolsList().add("第2个");

映射属性
private Name name//Name是自定义的类
Name.java
@Embeddable
public class Name {
@Column
private String first;
@Column
private String last;
@Parent
private Picture picture;

public Name() {
// TODO Auto-generated constructor stub
}

public Name(String first, String last) {
this.first = first;
this.last = last;
}
picture.setName2(new Name("cray", "zed"));
第二种方法:无需在组件类上使用@Embeddable注解,而是直接在持久化类中使用@Embedded注解修饰组件属性
Picture.java
@Embedded
@AttributeOverrides({
@AttributeOverride(name="first",column=@Column(name="person_firstname")),
@AttributeOverride(name="last",column=@Column(name="person_lastname"))
})
private Name name;

组件属性为集合
组件类包括了List、Set、Map等集合属性,直接在 组件类中使用@ElementCollection修饰集合属性,并使用@CollectionTable指定保存集合属性的数据表
@ElementCollection(targetClass = Integer.class)
@CollectionTable(name = "power_inf", joinColumns = @JoinColumn(name = "picture_name_id", nullable = false))
@MapKeyColumn(name = "name_aspect")
@Column(name = "map_power", nullable = false)
@MapKeyClass(String.class)
private Map<String, Integer> power = new HashMap<String, Integer>();
集合属性的元素为组件
对于集合元素是组件的集合属性,依然可以用@ElementCollection修饰集合属性,使用@CollectionTable映射保存集合属性的表。对于带索引的集合,List使用@OrderColumn
,Map使用@MapKeyColumn映射索引列
组件作为复合主键
组件类实现java.io.Serializable接口,并重写equals()和hashCode()方法
当持久化类使用组件作为复合主键时,需要使用@EmbeddedId来修饰该主键( @Embedded用于修饰普通的组件属性
多列作为联合主键
组件类实现java.io.Serializable接口,并重写equals()和hashCode()方法
分别用两个@Id修饰两个成员变量

Hibernate的关联映射
N-1关联
(单向/双向)
需要在N的一端使用@ManyToOne修饰代表关联实体的属性
@ManyToOne
->cascade:指定Hibernate对关联实体采用怎样的级联策略
->CascadeType.ALL:指定Hibernate将所有的持久化操作都级联到关联实体
->CascadeType.MERGE:指定Hibernate将merge操作级联到关联实体
->CascadeType.PERSIST
->CascadeType.REFRESH
->CascadeType.REMOVE
->fetch:指定住区关联实体时的抓取策略
->FetchType.EAGER
->FetchType.LAZY
->optional:指定关联关系是否可选
->targetEntity:指定关联实体的类名,默认Hibernate将通过反射来判断关联实体的类名
无连接表的N-1关联
Person1N.class
@Entity
@Table(name = "person1n_inf")
public class Person1N {
@Id
@Column(name = "person1n_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private int age;
// 定义Person实体关联的Address实体
@ManyToOne(targetEntity = Address.class)
// 映射外键列
@JoinColumn(name = "address_id", nullable = false)
@Cascade(CascadeType.ALL)
private Address address;
}
Address.class
@Entity
@Table(name = "address_inf")
public class Address {
@Id
@Column(name = "address_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int addressId;
private String addressDetail;
}
Person1NManager.java
Person1N p1 = new Person1N();
Address address = new Address("扬州宝应");
p1.setName("赵洪杰");
p1.setAge(22);
// 持久化Person对象
p1.setAddress(address);
session.persist(p1);
Address address2 = new Address("山东济南");
p1.setAddress(address2);
}


有连接表的N-1关联
显示使用@JoinTable注解来映射连接表
@JoinTable(name = "person_address", joinColumns = @JoinColumn(name =
"person_id", referencedColumnName = "person_id", unique = true),
// 指定连接表中address_id的外键列
inverseJoinColumns = @JoinColumn(name = "address_id",
referencedColumnName = "address_id"))
单向1-1关联@OneToOne
单向1-N关联@OneToMany用@JoinColum修饰Set集合属性
双向1-N
无连接宝的N一端需要增加@ManyToOne来修饰代表关联实体的属性,1的一段需要用@OneToMany注解来修饰代表关联实体的属性
对于制定了mappedBy属性的@OneToMany、@ManyToMany、@OneToOne注解,都不能与@JoinColumn或者@JoinTable同时修饰代表关联实体的属性
person.java
@OneToMany(targetEntity = Address.class, mappedBy = "person")
private Set<Address> address = new HashSet<Address>();
Address.java
@ManyToOne(targetEntity = Person.class)
@JoinColumn(name = "person_id", referencedColumnName = "person_id", nullable = false)
private Person person;
PersonManager
Session session = sFactory.openSession();
Transaction transaction = session.beginTransaction();
Person p1 = new Person();
p1.setName("小明");
p1.setAge(23);
session.save(p1);
Address address = new Address("扬州");
address.setPerson(p1);
session.persist(address);
Address address2 = new Address("苏州");
address2.setPerson(p1);
session.persist(address2);
transaction.commit();
整个类层次对应一个表的映射策略
@DiscriminatorColumn来配置辨别者列
//制定辨别者列的列名
@DiscriminatorColumn(name="person_type",discriminatorType=DiscriminatorType.STRING)
//制定实体对应的记录
@DiscriminatorValue("普通人")
子类只需要使用@DiscriminatorValue修饰就可以了
连接子类的映射策略
使用@Inheritance
使用@Inheritance必须制定strateg属性
InheritanceType.SINGLE_TABLE:整个类层次对应一个表的映射策略
inheritanceType.JOINED:连接子类的映射策略
父类中
@Inheritance(strategy = InheritanceType.JOINED)
子类不需要做修改
inheritanceType.TABLE_PER_CLASSS:每个具体类对应一个表的映射策略
不能使用GenerationType.IDENTITY、GenerationType.AUTO这两种主键生成策略
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@GenericGenerator(name="" strategy="hilo")
@GneratedValue(generator="")

Hibernate的批量处理
1批量插入
Hibernate的Session有一个必选的一级缓存,所有实例都在Session级别的缓存区进行缓存
session.flush();
session.clear();

上下文相关的Session
Hibernate3后增加了SesssionFactory.getCurrentSession()方法,Hibernate3.1开始,SessionFactory.getCurrentSession()的底层是可拔插的,Hibernate引入了CurrentSessionContext接口,并通过hibernate.current_session_context_class 参数来管理上下文相关的Session的底层实现
CurrentSessionContext接口有三个实现类:
org.hibernate.context.JTASessionContext:根据JTA来跟踪和界定上下文相关的Session
org.hibernate.context.ThreadLocalSessionContext :通过当前正在执行的线程来跟踪和界定上下文的Session,Hibernate的Session会随着getCurrentSession()方法自动打开,并随着事务提交自动关闭(推荐)
org.hibernate.context.ManagedSessionContext:通过当前正在执行的线程来跟踪和界定上下文的Session,单数程序需要使用这个类的静态方法将Session实例绑定、取消绑定,不会自动打开、flush或者关闭任何Session
指定Hibernate使用Session管理方式,在hibernate.cfg.xml增加如下片段
<property name="hibernate.current_session_context_class">thread</property>

二级缓存和查询缓存
开启二级缓存
<property name="hibernate.cache..use_second_level_cache">true</property>
设置缓存区的实现类
<property name="hibernate.cache.region.factory_class">缓存类</property>
ConcurrentHashMap org.hibernate.testing.cache.CachingRegionFactory
EhCache org.hibernate.cache.ehcache
Infinispan org.hibernate.cache.infinspan.InfinispanRegionFactory
指定Hibernate使用Session管理方式,在hibernate.cfg.xml增加如下片段
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.cache..use_second_level_cache">true</property>
(hibernate4.3-10Final必须使用JAVAEE7.0)
<property name="hibernate.cache.region.factory_class">net.sf.ehcache.Ehcache</property>
测试类
Session session = sFactory.getCurrentSession();
session.beginTransaction();
List list = session.createQuery("from MyNews mynews").list();
session.getTransaction().commit();
System.out.println("------------------------------------");
Session session2 = sFactory.getCurrentSession();
session2.beginTransaction();
MyNews myNews = (MyNews) session2.load(MyNews.class, 3);
System.out.println(myNews.getTitle());
session2.getTransaction().commit();
程序执行查询获取所有的News实体之后,SessionFactory会将这些实体缓存在二级缓存内,当程序粗体字代码根据主键值(3)加载特定的实体时,不惜要查询数据库,可以直接使用二级缓存中已有的实体
输出结果:
Hibernate:
select
mynews0_.mynews_id as mynews_i1_0_,
mynews0_.content as content2_0_,
mynews0_.title as title3_0_
from
mynews_inf mynews0_
------------------------------------
这是从二级缓存中获得的数据

Session级别的一级缓存是局部缓存,只对当前的Session有效;SessionFactory级别的二级缓存是全局缓存,对所有的Session都有效
对于Sesson级别的一级缓存而言,所有经它操作的实体,save()、update()、saveOrUpdate()保存对象,load()、get()、list()、iterate()、scroll()方法获得一个对象。该对象都会被放入Session级别的一级缓存中
将对象或集合从一级缓存中剔除:session.evict(Object obj)
判断是否在Session缓存中,Boolean session.contains(Object obj)
操作SessionFactory的二级缓存所缓存的实体,通过getCache()方法返回的一个Cached对象,通过该对象操作二级缓存中的实体、集合等
Cache cache=sessionFactory.getCache();
//清除指定的News对象
cache.evictEntity(News.class,id);
//清除所有的News对象
cache.evictEntityRegion(News.class)
//清除指定id的News所关联的参与者集合属性
cache,evicetCollection("News.actors",id)
//清除所有News关联的参与者的集合属性
cache.evictCollectionRegion("News.actors");
二级缓存的统计功能
<!--开启二级缓存的统计功能-->
<property name="hibernate.generate_statistics">true</property>
<!--设置使用结构化方式来维护缓存项-->
<property name="hibernate.cache.use_strutured_entries">true</property>
Map cacheEntries =sessionFactory.getStatistics().getSecondLevelCacheStatistice("类名").getEntries();
输出 cacheEntries
一级、二级缓存都是对整个实体进行缓存,不会缓存普通属性,使用查询缓存对普通属性进行换乘,
<!--启用查询缓存-->
<property name="hibernate.cache.use_query_cache">true</property>
session.createQuery("select news.title form News news"). setCacheabe(true).list();

Hibernate拦截器
使用拦截器
1.定义实现Interceptor接口的拦截器类
class MytInterceptor extends EmptyInterceptor{
当删除实体:onDelete()
当把持久化实体的状态同步到数据库时:onFlushDirty()
当加载持久化实体时:onLoad()
保存持久化实例:onSave()
持久化所做修改同步完成后:postFlush()
同步持久化所做修改之后:preFlush()
事务提交之前:beforeTransactionCompletion()
事务提交之后:afterTransactionComletion()
}
2.通过Session启动拦截器,或者通过Configuration启动全局拦截器
通过SessionFactory的openSession(Interceptor in)打开一个带局部拦截器的Session
通过Configuration的setInterceptor(Interceptor in)方法设置全局拦截器

事件系统
1、实现自己的事件监听类
->实现对用的监听器接口
->继承时间适配器
->继承系统默认的时间监听器:扩展特定方法(通常方法)
Hibernate提供了一个EventListenerRegistry接口,该接口提供了如下三类方法来注册事件监听器
->appendListeners():用于将自定义的事件监听器追加到系统默认的事件监听器序列的后面
->prependListeners():用于将自定义的事件监听器追加到系统默认的事件监听器序列的前面
->setListeners():用于使用自定义的事件监听器代替系统默认的事件监听序列
EventListenerRegistry elr=
//获取该SessionFactory的事件监听器注册器
((SessionFactoryImpl)sf).getServiceRegistry().getService(EventListenerRegistry.class);
//使用用户指定的拦截器序列代替系统默认的save拦截器序列
elr.setListeners(EventType.SAVE,MySaveListener.class)
//...
elr.setListeners(EventType.LOAD,MyLoadListener.class)
2、注册自定义时间监听器,代替系统默认的事件监听器


Hibernate较另一个持久层框架MyBatis。Hibernate倡导地入侵式的设计,完全采用普通的Java对象(POJO类),不要求PO继承某个超类或实现Hibernate的某个接口
ORM
对象/关系数据库映射,ORM框架的特征:完成面向对象的编程语言到关系数据库的映射,ORM框架是应用程序和数据库的桥梁
ORM工具提供了持久化类和数据表之间的映射关系,通过这种映射关系的过度,程序员可以很方便地通过持久化类实现对数据表的操作。
基本映射方式
数据表映射类:持久化类被映射到一个数据表,程序使用这个持久化类来创建实例、修改属性、删除实例时,系统自动回转换为对这个表进行CRUD操作
数据表——Model类
数据表的行映射对象:持久化类会生成很多实例,每个实例就对应数据表中的一行记录。
记录一 ——实例1
记录二 ——实例2
记录三 ——实例3
数据表的列映射对象的属性:
列1 列2 列3
l l l
l l l
属性1 属性2 属性3
流行的ORM框架:
JPA
MyBatis
TopLink

package com.hibernate.pojo;
@Entity
// 声明该类是一个Hibernate的持久化类
// 指定该类映射的表
public class News {
@Id
// 用于指定该类的标识属性(唯一标识该对象的属性,标识属性通常映射到数据表的主键列)
@GeneratedValue(strategy = GenerationType.IDENTITY)
// 用于指定主键生成策略,其中strategy属性指定了主键生成策略为IDENTITY策略,也就是采用自动增长的主键生成策略
private Integer id;
private String title;
private String content;
//getter、setter
}
Hibernate的配置文件既可以使用.properties属性文件,也可以使用XML文件配置

Hibernat配置文件的默认文件名为hibernate.cfg.xml,当程序调用Configuration对象的configure()方法时,Hibernate将自动加载盖文建
Hibernate不推荐用DriverManger来连接数据库,而是推荐使用数据原来管理数据库连接,这样能保证最好的性能.Hibernate推荐采用C3P0数据源


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">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<!-- 指定连接数据库所用的驱动 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 指定连接数据库的url,其中hibernate是本应用连接是数据库名 -->
<property name="connection.url">jdbc:mysql://localhost:3306/hibernate</property>
<!-- 指定连接数据库的用户名 -->
<property name="connection.username">root</property>
<!-- 指定连接数据库的密码 -->
<property name="connection.password">root</property>
<!-- 指定连接池里最大的连接数 -->
<property name="hibernate.c3p0.max_size">20</property>
<!-- 指定连接池里最小的连接数 -->
<property name="hibernate.c3p0.min_size">1</property>
<!-- 指定连接池里连接的超时时长 -->
<property name="hibernate.c3p0.timeout">5000</property>
<!-- 指定连接池里最大缓存多少个Statement对象 -->
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<property name="hibernate.c3p0.validate">true</property>
<!-- 指定数据库方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
(不能自动建表的原因是这里没有弄正确)
<!-- 根据需要自动创建数据表 -->
<property name="hbm2dd1.auto">update</property>
<!-- 显示Hibernate持久化操作所生成的SQL -->
<property name="show_sql">true</property>
<!-- 将SQL脚本进行格式化后再输出 -->
<property name="hibernate.format_sql">true</property>
<!-- 罗列所有持久化类的类名 -->
(所有需要持久化的类都必须在这罗列出来否则就会报 org.hibernate.MappingException: Unknown entity 这样的错误 )
<mapping class="com.hibernate.pojo.News"/>
</session-factory>
</hibernate-configuration>

public class NewsManger {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration()
// 不带参数的configure()默认加载hibernate.cfg.xml文件
// 如果传abc.xml作为参数,则不在加载hibernate.cfg.xml,改为加载abc.xml
.configure();
System.out.println("-----加载成功---");
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(conf .getProperties()).build();
// 以configuration实例创建SessionFactory实例
SessionFactory sFactory = conf.buildSessionFactory(serviceRegistry);
// 创建Session
Session session = sFactory.openSession();
// 开始事务
Transaction transaction = session.beginTransaction();
// 创建消息对象
News news = new News();
// 设置消息标题和消息内容
news.setTitle("标题");
news.setContent("消息内容," + "http://www.baidu.com");
// 保存消息
session.save(news);
// 提交事务
transaction.commit();
// 关闭Session
session.close();
sFactory.close();
}
注意事项:数据库、表、表单内容要先创建好,并且id要设置为自增长,否则要设置id值

SessionFactory:Hibernate关键对象,是单个数据库映射关系经过编译后的内存镜像,也是线程安全的。是生成Session的工厂,本身需要依赖ConnectionProvider。该对象可以再进程或者集群的级别上,为事务之间可以重用的数据提供可选的二级缓存
Session:是应用程序与持久存储层之间交互操作的一个单线程对象,特色Hibernate持久化操作的关键对象,所有的持久化对象必须在Session管理下才可以进行持久化操作,此对象生成存起很短。底层封装了JDBC连接,也是Transacation的工厂,Session持有必选的一级缓存,在显示执行flush之前,所有持久化操作都在缓存中的Session的Session处。
持久化对象(Persistent Object):系统创建的POJO实例,一旦与特定的Session关联,并对象数据表的指定记录,该对象就处于持久化状态,这一系列对象都被成为持久化对象。在程序中对持久化对象执行的修改,都将自动被转换为对持久层的修改。持久化对象完全可以是普通的JavaBeans/POJO,唯一的区别是他们正与一个Session关联
瞬态对象和脱管对象:系统通过New关键字创建的Java实例,没有与Session相关联,此时处于瞬态。瞬态实例可能是在被应用程序实例化后尚未进行持久化的对象。如果一个曾经持久化过的实例没如果Session被关闭则转化为托管状态
事务(Transaction):代表一次原子操作,它具有数据库事务的概念。Hibernate事务是对底层具体的JDBC、JTA以及CORBA事务的抽象。在某些情况下,一个Session之内可能包含多个Transaction对象,虽然事务操作是可选的,蛋所有持久化操作都应该在事务管理下进行,即使是只读操作
连接提供者(ConnectionProvider):JDBC的连接工厂没通过抽象将应用程序与底层的DataSource或者DriverManager隔离开。这个对象无需应用程序直接访问,尽在应用程序需要扩展时使用
事务工厂(Transcation):生成Transaction对象实例的工厂,该对象也无需用用程序直接访问,他负责对底层具体的事务进行封装,将底层具体的事务抽象成Hibernate事务

Hibernate持久化对象的状态演化图
备注:当Session调用带有*的方法时,所有与该Session关联的实例都将受到影响4

1.持久化实体:
Serializable save(Object obj):将obj对象变为持久化状态,该对象的属性将被保存到数据库
void persist(Object obj) :将obj对象转化为持久化状态,该对象的属性将被保存到数据库
Serializable save(Object obj,object pk):将obj对象保存到数据库,保存到数据库时,指定主键值
void persist(Object obj,Object obk):将obj对象保存到数据库,保存到数据库时,指定主键值
把一个瞬态实体变成持久化状态时,Hibernate会在底层对应的生成一台insert语句,这条语句的负责把该实体对应的数据记录插入到数据表
2.根据主键加载持久化实体
程序可以通过load()来加载一个持久化实体
News n=session.load(News.class,pk);//pk代表需要加载的持久化实例的标识属性
get方法类似
load()与get()区别
load()具有延迟加载的功能,load()方法不会立即访问数据库,当试图加载的记录不存在时,load()方法可能返回一个未初始化的代理对象,get()立即访问数据库,当记录不存在时,将直接返回Null
3.更新脱管实体
当程序修改托管对象的状态后,程序应该显示的使用新的Session对象来保存这些修改,Hibernate提供了update()、merge()和updateOrSave()方法来保存这些改变

Hibernate映射
@Entity :被该注释修饰的POJO就是一个实体。
@Table :该注释指定持久化类所映射的表
catalog 设置将持久化类映射表放入指定的catalog中
indexs 设置索引 是一个@Index注解数组
columnList 设置对那些建立索引,可指定多个数据列的列名
name 索引名字
unique 设置索引是否具有唯一性,boolean
name 持久化类映射的表的表名(不设置表名则与持久化的类的类名相同)
schema 将持久化类映射的表放入指定的schema中
uniqueConstraints 持久化类所映射的表的唯一约束,值是一个@UniqueConstraint注解数组
columNames 字符串数组,每个字符串元素代表一个数据列
@Proxy :指定一个接口,具有延迟加载时作为代理使用,也可以指定类名
@DynamicInsert: 指定用于插入记录的Insert语句是否在运行时生成,并且只插入非空字段,默认是false
@DynamicUpdate :指定用于更新记录的update语句是否在裕兴时动态生成,并且只更新改变过的字段,默认是false
打开后可以指定一下乐观的锁定策略
->OptimisticLockType.VERSION:检查version/timestamp字段( ☆建议,对性能来说是最好的选择,也是唯一能后处理在Session外进行托管操作的策略)
@SelectBeforeUpdate:指定Hibernate在update某个持久化对象之前是否需要先进行一次select。若为true则Hibernate可以保证只有当持久化对象的状态被修改过时,才会使用update来保存其状态。
@PolymorphismType:当采用TABLE_PER_CLASS继承映射策略时,该注解用于指定是否需要采用隐式多态查询。默认value值为PolymorphismType.IMPLICIT,即支持隐式多态查询
@Where:该注解的clause属性可以指定一个附加的SQL语句过滤条件,如果加入,啧不管采用Load(),get()还是其他的查询方法,只要试图加载该持久化类的对象时,该where条件就会声效,也就是说,只有符合该where条件的记录才会被拦截
@BatchSize:当Hibernate抓取集合属性或延迟加载的实体时,该注解的size属性指定每批抓取的实例数
@OptimisticLocking:该注解的type属性指定乐观锁定策略,Hibernate支持OptimisticLockType.ALL、OptimisticLockType.DIRTY、OpmisticLockType.NONE、OpmisticLockType.VERSION这4个枚举值,默认值是OptimisticLockType.VERSION
@Check:可通过constrains指定一个SQL表达式,用于该持久化类所对应的表指定一个Check约束
@Subselect:该注解用于映射不可变的、只读实体。(将数据库的子查询映射成Hibernate持久化对象,当需要使用视图来代替数据表时,比较有用)

映射属性
被@Entity修饰的持久化类的所有属性都会被映射到底层数据表,为指定某个属性所映射的数据列的详细信息,可以在实体类中使用@Column修饰该属性
columnDefinition SQL字符串,指定创建该数据列的SQL语句
Insertabel 指定该列是否包含在Hibernate声场的insert语句的列表中,默认为true
length 指定该列所能保存的数据的最大长度默认255
name 指定列名,默认与@Column修饰的成员变量的名相同
table 指定该列所属的表名,当需要用多个表来保存一个实体时,需要指定该属性
unique 是否具有唯一约束
@Formula:该注解的value属性可指定一个SQL表达式。
@Generated:设置该属性映射的数据列的值是否由数据库生成,该注解的value属性可以接受GenerationTime.NEVER(不由数据库生成),GenerationTime.INSERT(该属性值在执行insert语句时生成,但不会在执行update语句时重新生成)和GenerationTime.ALWAYS该属性值在执行insert和unpdate语句时都会被重新生成)这三个值其中之一
@Formula的value属性允许对象属性包含表达式,包括运用sum、average、max函数求值的结果
value="(select avg(p,price) from Product p)"/>
或者是一个表的查询结果
value="select cur.name from currency cur where cur.id=currencyID"/>
*注意点
value="(sql)"英文括号不能少
value="()"括号里面是SQL表达式,SQL表达式中的列名与表名都应该和数据库对应,而不是和持久化对象的属性对应
如果需要在@Formula的value属性中使用参数,直接使用where cur.id=currencyID形式,其中currencyID就是参数,当前持久化对象的currencyID属性将作为参数传入
@Formula("(select concat(nt.title,nt.content)"+ "from news_table nt where nt.id= id)")
private String fullContent;

News news2 = (News) session.get(News.class, 1);
System.out.println(news2.getFullContent());
输出的是计算结果,且没有为fullContent属性创建对应的数据列
@Transient修饰不想吃就保存的属性
@Transient
private String content;
@Enumerated修饰枚举类型的属性
@Enumerated(EnumType.ORDINAL)
@Column(name="happen_season")
private Season happenSeason;(Season是自定义类 public enum Season{****})
@Enumerated(EnumType.ORDINAL)修饰了枚举类型的属性,底层数据库会保存数值序号来代表枚举值
使用@Lob@Baisc修饰大数据类型的属性
@Lob
@Basic(fetch=FetchType.LAZY)
private byte[] pic;

File file = new File("leimu.jpg");
byte[] content = new byte[(int) file.length()];
new FileInputStream(file).read(content);
picture.setPic(content);
(@Basic:Hibernate加载Picture对象时并不立即加载他的pic属性,而是只加载一个“虚拟”的代理,等到程序正正需要pic属性时才从底层数据表中加载数据(代理模式))
@Basic
->fetch 指定是否延迟加载该属性,FetchType.EAGER(立即加载)FetchType.LAZY(延迟加载)
->optional:指定该属性映射的数据列是否允许使用Null
@Temporal修饰日期类型的属性
@Temporal(TemporalType.DATE)
private Date birth;

映射主键
@GeneratedValue
->strategy
->GenerationType.AUTO:Hibernate自动选择最适合底层数据库的主键生成策略
->GenerationType.IDENTITY:对于MySQL,SQL Server这样是数据库,选择自增长的主键生成策略
->GenerationType.SEQUENCE:对于Oracle这样的数据库,选择使用基于Sequence的主键生成策略,应该与@SequenceGenerator一起使用
->GenerationType.TABLE :使用辅助表来生成主键,应与@TableGenerator一起使用
->generator:当使用GenerationType.SEQUENCE,GenerationType.TABLE主键生成策略时,该属性引用@SequenceGenerator,@TableGenerator所定义的生成器的名称

使用Hibernate的主键生成策略
@GenericGenerator
->name:必需属性,设置主键生成器的名称,可以被@GeneratedValue的generator属性引用
->strategy:必需属性,设置该主键生成器的主键生成策略
->increment :long,short,int类型主键生成的唯一标识
->identity:在DB2、MySQL、SQLServer等提供identity(自增长)主键支持的数据表中适用
->sequence:在DB2,Oracle等提供Sequence支持的数据表中适用
->uuid:用一个128位的UUID算法生成的字符串类型的标识符,这在一个网络中是唯一的(IP地址也作为算法的数据源)。UUID被编码为一个32位十六进制的字符串
->guid:在SQL Server和MySQL中适用数据库生成的GUID字符串
->native:根据底层数据库的能力选择ident、sequence或者hilo中的一个
->assigned:让程序在save()之前为对象分配一个标识符。
->select:通过数据库触发器选择某个唯一主键的行,并返回其主键值组颇为标识属性值
->foreign:表明直接使用另一个关联的对象的标识属性值。

映射集合属性
@ElementCollection
->fetch :指定该实体对集合属性的抓取策略,FetchType.EAGER(立即抓取)和FetchType.;LAZY(延迟抓取)默认延迟
->targetClass:指定集合属性中集合元素的类型
Hibernate使用标准的@CollectionTable注解映射保存集合属性的表
@JoinColumn注解专门用于定义外键列
@Id
@Column(name = "picture_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
@Lob
private byte[] pic;
@ElementCollection(targetClass = String.class)
@CollectionTable(name = "school_inf", joinColumns = @JoinColumn(name = "picture_id", nullable = false))
@Column(name = "school_name")
@OrderColumn(name = "list_order")
private List<String> schoolsList = new ArrayList<String>();

picture.getSchoolsList().add("第1个");
picture.getSchoolsList().add("第2个");

映射属性
private Name name//Name是自定义的类
Name.java
@Embeddable
public class Name {
@Column
private String first;
@Column
private String last;
@Parent
private Picture picture;

public Name() {
// TODO Auto-generated constructor stub
}

public Name(String first, String last) {
this.first = first;
this.last = last;
}
picture.setName2(new Name("cray", "zed"));
第二种方法:无需在组件类上使用@Embeddable注解,而是直接在持久化类中使用@Embedded注解修饰组件属性
Picture.java
@Embedded
@AttributeOverrides({
@AttributeOverride(name="first",column=@Column(name="person_firstname")),
@AttributeOverride(name="last",column=@Column(name="person_lastname"))
})
private Name name;

组件属性为集合
组件类包括了List、Set、Map等集合属性,直接在 组件类中使用@ElementCollection修饰集合属性,并使用@CollectionTable指定保存集合属性的数据表
@ElementCollection(targetClass = Integer.class)
@CollectionTable(name = "power_inf", joinColumns = @JoinColumn(name = "picture_name_id", nullable = false))
@MapKeyColumn(name = "name_aspect")
@Column(name = "map_power", nullable = false)
@MapKeyClass(String.class)
private Map<String, Integer> power = new HashMap<String, Integer>();
集合属性的元素为组件
对于集合元素是组件的集合属性,依然可以用@ElementCollection修饰集合属性,使用@CollectionTable映射保存集合属性的表。对于带索引的集合,List使用@OrderColumn
,Map使用@MapKeyColumn映射索引列
组件作为复合主键
组件类实现java.io.Serializable接口,并重写equals()和hashCode()方法
当持久化类使用组件作为复合主键时,需要使用@EmbeddedId来修饰该主键( @Embedded用于修饰普通的组件属性
多列作为联合主键
组件类实现java.io.Serializable接口,并重写equals()和hashCode()方法
分别用两个@Id修饰两个成员变量

Hibernate的关联映射
N-1关联
(单向/双向)
需要在N的一端使用@ManyToOne修饰代表关联实体的属性
@ManyToOne
->cascade:指定Hibernate对关联实体采用怎样的级联策略
->CascadeType.ALL:指定Hibernate将所有的持久化操作都级联到关联实体
->CascadeType.MERGE:指定Hibernate将merge操作级联到关联实体
->CascadeType.PERSIST
->CascadeType.REFRESH
->CascadeType.REMOVE
->fetch:指定住区关联实体时的抓取策略
->FetchType.EAGER
->FetchType.LAZY
->optional:指定关联关系是否可选
->targetEntity:指定关联实体的类名,默认Hibernate将通过反射来判断关联实体的类名
无连接表的N-1关联
Person1N.class
@Entity
@Table(name = "person1n_inf")
public class Person1N {
@Id
@Column(name = "person1n_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private int age;
// 定义Person实体关联的Address实体
@ManyToOne(targetEntity = Address.class)
// 映射外键列
@JoinColumn(name = "address_id", nullable = false)
@Cascade(CascadeType.ALL)
private Address address;
}
Address.class
@Entity
@Table(name = "address_inf")
public class Address {
@Id
@Column(name = "address_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int addressId;
private String addressDetail;
}
Person1NManager.java
Person1N p1 = new Person1N();
Address address = new Address("扬州宝应");
p1.setName("赵洪杰");
p1.setAge(22);
// 持久化Person对象
p1.setAddress(address);
session.persist(p1);
Address address2 = new Address("山东济南");
p1.setAddress(address2);
}


有连接表的N-1关联
显示使用@JoinTable注解来映射连接表
@JoinTable(name = "person_address", joinColumns = @JoinColumn(name =
"person_id", referencedColumnName = "person_id", unique = true),
// 指定连接表中address_id的外键列
inverseJoinColumns = @JoinColumn(name = "address_id",
referencedColumnName = "address_id"))
单向1-1关联@OneToOne
单向1-N关联@OneToMany用@JoinColum修饰Set集合属性
双向1-N
无连接宝的N一端需要增加@ManyToOne来修饰代表关联实体的属性,1的一段需要用@OneToMany注解来修饰代表关联实体的属性
对于制定了mappedBy属性的@OneToMany、@ManyToMany、@OneToOne注解,都不能与@JoinColumn或者@JoinTable同时修饰代表关联实体的属性
person.java
@OneToMany(targetEntity = Address.class, mappedBy = "person")
private Set<Address> address = new HashSet<Address>();
Address.java
@ManyToOne(targetEntity = Person.class)
@JoinColumn(name = "person_id", referencedColumnName = "person_id", nullable = false)
private Person person;
PersonManager
Session session = sFactory.openSession();
Transaction transaction = session.beginTransaction();
Person p1 = new Person();
p1.setName("小明");
p1.setAge(23);
session.save(p1);
Address address = new Address("扬州");
address.setPerson(p1);
session.persist(address);
Address address2 = new Address("苏州");
address2.setPerson(p1);
session.persist(address2);
transaction.commit();
整个类层次对应一个表的映射策略
@DiscriminatorColumn来配置辨别者列
//制定辨别者列的列名
@DiscriminatorColumn(name="person_type",discriminatorType=DiscriminatorType.STRING)
//制定实体对应的记录
@DiscriminatorValue("普通人")
子类只需要使用@DiscriminatorValue修饰就可以了
连接子类的映射策略
使用@Inheritance
使用@Inheritance必须制定strateg属性
InheritanceType.SINGLE_TABLE:整个类层次对应一个表的映射策略
inheritanceType.JOINED:连接子类的映射策略
父类中
@Inheritance(strategy = InheritanceType.JOINED)
子类不需要做修改
inheritanceType.TABLE_PER_CLASSS:每个具体类对应一个表的映射策略
不能使用GenerationType.IDENTITY、GenerationType.AUTO这两种主键生成策略
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@GenericGenerator(name="" strategy="hilo")
@GneratedValue(generator="")

Hibernate的批量处理
1批量插入
Hibernate的Session有一个必选的一级缓存,所有实例都在Session级别的缓存区进行缓存
session.flush();
session.clear();

上下文相关的Session
Hibernate3后增加了SesssionFactory.getCurrentSession()方法,Hibernate3.1开始,SessionFactory.getCurrentSession()的底层是可拔插的,Hibernate引入了CurrentSessionContext接口,并通过hibernate.current_session_context_class 参数来管理上下文相关的Session的底层实现
CurrentSessionContext接口有三个实现类:
org.hibernate.context.JTASessionContext:根据JTA来跟踪和界定上下文相关的Session
org.hibernate.context.ThreadLocalSessionContext :通过当前正在执行的线程来跟踪和界定上下文的Session,Hibernate的Session会随着getCurrentSession()方法自动打开,并随着事务提交自动关闭(推荐)
org.hibernate.context.ManagedSessionContext:通过当前正在执行的线程来跟踪和界定上下文的Session,单数程序需要使用这个类的静态方法将Session实例绑定、取消绑定,不会自动打开、flush或者关闭任何Session
指定Hibernate使用Session管理方式,在hibernate.cfg.xml增加如下片段
<property name="hibernate.current_session_context_class">thread</property>

二级缓存和查询缓存
开启二级缓存
<property name="hibernate.cache..use_second_level_cache">true</property>
设置缓存区的实现类
<property name="hibernate.cache.region.factory_class">缓存类</property>
ConcurrentHashMap org.hibernate.testing.cache.CachingRegionFactory
EhCache org.hibernate.cache.ehcache
Infinispan org.hibernate.cache.infinspan.InfinispanRegionFactory
指定Hibernate使用Session管理方式,在hibernate.cfg.xml增加如下片段
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.cache..use_second_level_cache">true</property>
(hibernate4.3-10Final必须使用JAVAEE7.0)
<property name="hibernate.cache.region.factory_class">net.sf.ehcache.Ehcache</property>
测试类
Session session = sFactory.getCurrentSession();
session.beginTransaction();
List list = session.createQuery("from MyNews mynews").list();
session.getTransaction().commit();
System.out.println("------------------------------------");
Session session2 = sFactory.getCurrentSession();
session2.beginTransaction();
MyNews myNews = (MyNews) session2.load(MyNews.class, 3);
System.out.println(myNews.getTitle());
session2.getTransaction().commit();
程序执行查询获取所有的News实体之后,SessionFactory会将这些实体缓存在二级缓存内,当程序粗体字代码根据主键值(3)加载特定的实体时,不惜要查询数据库,可以直接使用二级缓存中已有的实体
输出结果:
Hibernate:
select
mynews0_.mynews_id as mynews_i1_0_,
mynews0_.content as content2_0_,
mynews0_.title as title3_0_
from
mynews_inf mynews0_
------------------------------------
这是从二级缓存中获得的数据

Session级别的一级缓存是局部缓存,只对当前的Session有效;SessionFactory级别的二级缓存是全局缓存,对所有的Session都有效
对于Sesson级别的一级缓存而言,所有经它操作的实体,save()、update()、saveOrUpdate()保存对象,load()、get()、list()、iterate()、scroll()方法获得一个对象。该对象都会被放入Session级别的一级缓存中
将对象或集合从一级缓存中剔除:session.evict(Object obj)
判断是否在Session缓存中,Boolean session.contains(Object obj)
操作SessionFactory的二级缓存所缓存的实体,通过getCache()方法返回的一个Cached对象,通过该对象操作二级缓存中的实体、集合等
Cache cache=sessionFactory.getCache();
//清除指定的News对象
cache.evictEntity(News.class,id);
//清除所有的News对象
cache.evictEntityRegion(News.class)
//清除指定id的News所关联的参与者集合属性
cache,evicetCollection("News.actors",id)
//清除所有News关联的参与者的集合属性
cache.evictCollectionRegion("News.actors");
二级缓存的统计功能
<!--开启二级缓存的统计功能-->
<property name="hibernate.generate_statistics">true</property>
<!--设置使用结构化方式来维护缓存项-->
<property name="hibernate.cache.use_strutured_entries">true</property>
Map cacheEntries =sessionFactory.getStatistics().getSecondLevelCacheStatistice("类名").getEntries();
输出 cacheEntries
一级、二级缓存都是对整个实体进行缓存,不会缓存普通属性,使用查询缓存对普通属性进行换乘,
<!--启用查询缓存-->
<property name="hibernate.cache.use_query_cache">true</property>
session.createQuery("select news.title form News news"). setCacheabe(true).list();

Hibernate拦截器
使用拦截器
1.定义实现Interceptor接口的拦截器类
class MytInterceptor extends EmptyInterceptor{
当删除实体:onDelete()
当把持久化实体的状态同步到数据库时:onFlushDirty()
当加载持久化实体时:onLoad()
保存持久化实例:onSave()
持久化所做修改同步完成后:postFlush()
同步持久化所做修改之后:preFlush()
事务提交之前:beforeTransactionCompletion()
事务提交之后:afterTransactionComletion()
}
2.通过Session启动拦截器,或者通过Configuration启动全局拦截器
通过SessionFactory的openSession(Interceptor in)打开一个带局部拦截器的Session
通过Configuration的setInterceptor(Interceptor in)方法设置全局拦截器

事件系统
1、实现自己的事件监听类
->实现对用的监听器接口
->继承时间适配器
->继承系统默认的时间监听器:扩展特定方法(通常方法)
Hibernate提供了一个EventListenerRegistry接口,该接口提供了如下三类方法来注册事件监听器
->appendListeners():用于将自定义的事件监听器追加到系统默认的事件监听器序列的后面
->prependListeners():用于将自定义的事件监听器追加到系统默认的事件监听器序列的前面
->setListeners():用于使用自定义的事件监听器代替系统默认的事件监听序列
EventListenerRegistry elr=
//获取该SessionFactory的事件监听器注册器
((SessionFactoryImpl)sf).getServiceRegistry().getService(EventListenerRegistry.class);
//使用用户指定的拦截器序列代替系统默认的save拦截器序列
elr.setListeners(EventType.SAVE,MySaveListener.class)
//...
elr.setListeners(EventType.LOAD,MyLoadListener.class)
2、注册自定义时间监听器,代替系统默认的事件监听器


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值