hibernate详解

1.关联关系



Inverse:Inverse属性的可能值是true或者false,默认为false:
    false代表由己方来维护关系,true代表由对方来维护关系。在一个关系中,只能由一方来维护关系,否则会出问题;同时也必须由一方来维护关系,否则会出现双方互相推卸责任,谁也不管。
    负责更新关联键的值
    // inverse = 'false',fatherid为关联键
Hibernate: update child set fatherid =? where ID=?  
Hibernate: update child set fatherid =? where ID=? 
Cascade代表是否执行级联操作:把关联的对象一起操作(更新、删除等)
级联风格 Session中的方法
persist persist()
merge merge()
save-update save()、update()、saveOrUpdate()
delete delete()
lock lock()
refresh refresh()
evict evict()
replicate replicate()


fetch ,指定关联对象抓取的方式,可以设置fetch = "select" 和 fetch = "join"。
fetch = "select"是在查询的时候先查询出一端的实体,然后在根据一端的查询出多端的实体,会产生1+n条sql语句;
fetch = "join"是在查询的时候使用外连接进行查询,不会差生1+n的现象。
lazy概念:只有真正使用该对象时,才会创建。对于hibernate而言,真正使用时才会发出SQL语句
1、在集合中定义:
<set name="name" lazy="true/false/extra" >
默认为true
默认为true情况下,只要使用到了Set对象,就会把整个set全部查询出来。
false情况下,不使用Lazy,查询Lazy所属的对象时,set就会被查询上来。需要类增强工具。
extra情况下,比较智能,根据查询的内容,生成不同的SQL语句。效率会高一些。
例子:在我们前边多对一的关系中(部门与员工):
Department.hbm.xml:
<set name="emps" inverse="true" lazy="false">  
           <key column="depart_id" />  
           <one-to-many class="Employee" />  
       </set>  
通过这个可以关闭默认的懒加载
2、单端关联
<many-to-one name="name" lazy="false/proxy/no-proxy">
默认为proxy
false:不使用Lazy。
no-proxy:需要类增强工具
<many-to-one name="depart" column="depart_id" lazy="false"/> 


lazy 和fetch配合使用:
1、当lazy="true" fetch = "select" 的时候 , 这个时候是使用了延迟策略,开始只查询出一端实体,多端的不会查询,只有当用到的时候才会发出sql语句去查询 ;
2、当lazy="false" fetch = "select" 的时候 , 这个时候是使没有用延迟策略,同时查询出一端和多端,同时产生1+n条sql.
3、当lazy="true"/lazy="false" fetch = "join"的时候,自己认为这个时候延迟已经没有什么用了,因为采用的是外连接查询,同时把一端和多端都查询出来了,延迟没有起作用。


xml声明方式
一对一主键关联(两个实体对象的主键一样,以表明它们之间的一一对应关系,不需要多余的外键字段来维护关系,仅通过主键来关联)
表:PERSION(人){id、name、age}、CARD(身份证){id、cardNO}
card的主键依赖person的主键。
单向:
public class Card implements Serializable{
   private static final long serialVersionUID = 1L;
private long id;
private String cardNo;
private Person person;//人  一对一关联
。。。
}
<class name="com.cy.beans.Card" table="card" catalog="j2ee">   <!-- catalog数据库 -->
<id name="id" type="java.lang.Long" column="id"><!-- 此行的ID,为对象的属性ID -->
<generator class="foreign"><!-- foreign主键生成器 -->
<param name="property">person</param><!--类属性  -->
</generator>
</id>


<property name="cardNo" type="java.lang.String" column="cardNo"/>
<!--站在从对象的一方来设置交出约束  -->
<!-- name:一对一节点  ,constrained: 约束(必须为true) -->
<one-to-one name="person"  class="com.cy.beans.Person" constrained="true"></one-to-one>
</class>
双向:为persion添加card对象。
public class Person {  
   private int id;  
   private String name;  
   private Card Card;
。。
}
<class name="Person" table="t_person">  
       <id name="id">  
       <!-- 采用foreign生成策略,forgeign会取得另外一个关联对象的标识 -->  
           <generator class="uuid"/>  
       </id>  
       <property name="name"></property>  
       <one-to-one name="card" class="Card"></one-to-one>
   </class>  


一对一外键关联:person表中有cardid(card表的主键)
单向
<class name="Person" table="t_person">  
       <id name="id">  
           <generator class="native" ></generator>  
       </id>  
       <property name="name"></property>  
       <!-- 由于它是一对一的唯一外键关联,它是多对一关联的特例,注释可以直接写成多对一关联-->  
       <!-- idCard属性,表达的是本对象与IdCard的多对一关系。 -->  
       <many-to-one name="idCard" class="IdCard" column="CardId" unique="true"></many-to-one>      
   </class>  
public class Person {  
   private int id;  
   private String name;  
   private Card Card;
。。
}
双向:card类里面添加person类
<class name="IdCard" table="t_idCard">  
       <id name="id">  
           <generator class="native"></generator>  
       </id>  
       <property name="cardNo"></property>  
       <!-- 一对一唯一外键关联双向采用<one-to-one>标签来映射,必须指定<one-to-one>  
           标签中的property-ref属性为关系字段在类里的名称
        -->  
       <one-to-one name="person" class="Person" property-ref="idCard"></one-to-one>  
   </class>
public class IdCard {  
   private int id;  
   private String cardNo;  
   private Person person;
。。。
}
一对多单向关联映射:多对一和一对多的区别在于维护的关系不同:多对一:多端维护一端的关系,一对多:一端维护多端的关系
表:person(人){id、name、age、groupid};group(组织表){id、name}
单向:
 <class name="...Group" table="t_group">  
       <id name="id">  
           <generator class="native"/>  
       </id>  
       <property name="name"/>  
       <!-- users属性,表达的是本对象与User的一对多的关系 -->  
       <set name="users">  
            <!--person表中的关联键-->  
           <key column="groupid"/>  
           <one-to-many class="...person"/>  
       </set>  
   </class>  
public class Group {  
   private int id;  
   private String name;  
   private Set users; 
....
}
双向:在person类中添加Group类
<class name="User" table="t_user">  
       <id name="id">  
           <generator class="native"/>  
       </id>  
       <property name="name"/>  
       <!-- groups属性,表达的是本对象与Group的多对一的关系 -->  
       <many-to-one name="groups" class="Group" column="groupid"/>  
   </class>
public class User {  
   private int id;  
   private String name;  
   private Group groups;
...
}


多对多关联映射:一个用户(User)对多个角色(Role),一个角色对多个用户。
表:user(用户表){id、name};user_role(用户角色关联表){user_id、role_id};role(角色表){id、name}
单向:
<class name="User" table="t_user">  
<id name="id">  
<generator class="native"></generator>  
</id>  
<property name="name"></property>  
<!-- roles属性,表达的是本对象(User)与Role的多对多的关系 -->    
<set name="roles" table="t_user_role">  
<!--关联表中的user_id-->  
<key column="user_id"></key>  
<!--关联表中的role_id-->  
<many-to-many class="Role" column="role_id"></many-to-many>  
</set>  
</class>  
public class User {  
private int id;  
private String name;  
private Set roles; 
...

双向:
<class name="Role" table="t_role">  
<id name="id">  
<generator class="native"></generator>  
</id>  
<property name="name"></property>     
<set name="users" table="t_user_role">                
<key column="role_id"></key>  
<many-to-many class="User" column="user_id"></many-to-many>  
</set>  
</class> 
public class Role {  
private int id;  
private String name;  
private Set users;
...
}

注解方式:
mappedBy:指定对方维护关系,对方类没有用@joinColum指定关联的字段,会创建字段(对方表+"_"+id)
IdCard类:
@OneToOne( mappedBy="person",fetch=FetchType.LAZY)  
private IdCard idCard;


Person类:
@OneToOne(fetch=FetchType.LAZY)  


@joinColum:在本类的关联字段上指定本表的关联字段
@JoinColumn(name="p_id")
private Person person;



一对多的映射(one-to-many)


这里我们定义了两个实体类,一个是ClassRoom,一个是Student,这两者是一对多的关联关系。


ClassRoom类:


@Entity
@Table(name="t_classroom")
public class ClassRoom
{
   @Id
   @GeneratedValue
   private int id;
   private String className;
   @OneToMany(mappedBy="room")  --->  OneToMany指定了一对多的关系,mappedBy="room"指定了由多的那一方来维护关联关系,mappedBy指的是多的一方对1的这一方的依赖的属性,(注意:如果没有指定由谁来维护关联关系,则系统会给我们创建一张中间表)
   @LazyCollection(LazyCollectionOption.EXTRA)  --->  LazyCollection属性设置成EXTRA指定了当如果查询数据的个数时候,只会发出一条 count(*)的语句,提高性能.当使用集合时 才执行select
    LazyCollection属性设置成FALSE,会立即查询。TRUE,当使用的时候才查询
   private Set<Student> students;
   ...
}
Student类:


@Entity
@Table(name="t_student")
public class Student
{
   @Id
   @GeneratedValue
   private int id;
   private String name;
   private int age;
   @ManyToOne(fetch=FetchType.LAZY)  ---> ManyToOne指定了多对一的关系,fetch=FetchType.LAZY属性表示在多的那一方通过延迟加载的方式加载对象(默认不是延迟加载)
   @JoinColumn(name="rid")  --->  通过 JoinColumn 的name属性指定了外键的名称 rid (注意:如果我们不通过JoinColum来指定外键的名称,系统会给我们声明一个名称)
   private ClassRoom room;
   ...
}
一对一映射(One-to-One)
主键关联:
public class Person {  


     @Id  
     @Column(name="id")  
     @GenericGenerator(name="uuidGenerator",strategy="uuid")  
     @GeneratedValue(generator="uuidGenerator")  
     private String id;    
       
     @Column(name="name")  
     private String name;    
         
       /**  
      * cascade表示级联的意思  
      * FetchMode.SELECT的方式表示,查询与之关联的数据的时候,使用select的方式,而不是左外连接,与在  
      * 在@OneToOne()中间加上fetch=FetchType.LAZY所表达出来的意思是一样的。  
      * @PrimaryKeyJoinColumn必须在生产主键的一方指定,不然会在被关联的一方多出一列  
      */  
     @OneToOne(cascade=CascadeType.ALL)  
     @Fetch(value=FetchMode.SELECT)    
     @PrimaryKeyJoinColumn
     private IdCard idCard;
   }
   @Entity  
@Table(name="cards")  
public class IdCard {  
@Id  
@Column(name="id")  
@GenericGenerator(name="foreignGenerator", strategy="foreign", parameters={@Parameter(value="person", name="property")})  
@GeneratedValue(generator="foreignGenerator")  
private String id;    


@Column(name="card_num")  
private String cardNum;    


/**  
* mappedBy="idCard",其中idCard为Person类中的属性名  
*/  
@OneToOne(fetch=FetchType.LAZY)  
private Person person;  
}
外键关联:
一对一关系这里定义了一个Person对象以及一个IDCard对象


Person类:


@Entity
@Table(name="t_person")
public class Person
{
   @Id
   @GeneratedValue
   private int id;
   private String name;
   @OneToOne(mappedBy="person")  --->  指定了OneToOne的关联关系,mappedBy同样指定由对方来进行维护关联关系
   private IDCard card;
   ...
}
IDCard类:


@Entity
@Table(name="t_id_card")
public class IDCard
{
   @Id
   @GeneratedValue
   private int id;
   private String no;
   @OneToOne  --->  OnetoOne指定了一对一的关联关系,一对一中随便指定一方来维护映射关系,这里选择IDCard来进行维护
   @JoinColumn(name="pid")  --->  指定外键的名字 pid
   private Person person;
   ...
}
注意:在判断到底是谁维护关联关系时,可以通过查看外键,哪个实体类定义了外键,哪个类就负责维护关联关系。


Many-to-Many映射(多对多映射关系)


多对多这里通常有两种处理方式,一种是通过建立一张中间表,然后由任一一个多的一方来维护关联关系,另一种就是将多对多拆分成两个一对多的关联关系


1.通过中间表由任一一个多的一方来维护关联关系


Teacher类:


@Entity
@Table(name="t_teacher")
public class Teacher
{
   @Id
   @GeneratedValue
   private int id;
   private String name;
   @ManyToMany(mappedBy="teachers")  --->  表示由Course那一方来进行维护
   private Set<Course> courses;
   ...
}
Course类:


@Entity
@Table(name="t_course")
public class Course
{
   @Id
   @GeneratedValue
   private int id;
   private String name;
   @ManyToMany   ---> ManyToMany指定多对多的关联关系
   @JoinTable(name="t_teacher_course", joinColumns={ @JoinColumn(name="cid")}, 
   inverseJoinColumns={ @JoinColumn(name = "tid") })  --->  因为多对多之间会通过一张中间表来维护两表直接的关系,所以通过 JoinTable 这个注解来声明,name就是指定了中间表的名字,JoinColumns是一个 @JoinColumn类型的数组,表示的是我这方在对方中的外键名称,我方是Course,所以在对方外键的名称就是 rid,inverseJoinColumns也是一个 @JoinColumn类型的数组,表示的是对方在我这放中的外键名称,对方是Teacher,所以在我方外键的名称就是 tid
   private Set<Teacher> teachers;
   ...
}
2.将Many-to-Many拆分成两个One-to-Many的映射(Admin、Role、AdminRole)


Admin类:


@Entity
@Table(name="t_admin")
public class Admin
{
   @Id
   @GeneratedValue
   private int id;
   private String name;
   @OneToMany(mappedBy="admin")  --->  OneToMany关联到了AdminRole这个类,由AdminRole这个类来维护多对一的关系,mappedBy="admin"
   @LazyCollection(LazyCollectionOption.EXTRA)  
   private Set<AdminRole> ars;
   ...
}
Role类:


@Entity
@Table(name="t_role")
public class Role
{
   @Id
   @GeneratedValue
   private int id;
   private String name;
   @OneToMany(mappedBy="role")  --->  OneToMany指定了由AdminRole这个类来维护多对一的关联关系,mappedBy="role"
   @LazyCollection(LazyCollectionOption.EXTRA)
   private Set<AdminRole> ars;
  ...
}
AdminRole类:


@Entity
@Table(name="t_admin_role")
public class AdminRole
{
@Id
   @GeneratedValue
   private int id;
   private String name;
   @ManyToOne  --->  ManyToOne关联到Admin
   @JoinColumn(name="aid")  
   private Admin admin;
   @ManyToOne  --->  
   @JoinColumn(name="rid")
   private Role role;
   ...
}


2.主键生成策略
Hibernate各种主键生成策略与配置详解
1、assigned
主键由外部程序负责生成,在 save() 之前必须指定一个。Hibernate不负责维护主键生成。与Hibernate和底层数据库都无关,可以跨数据库。在存储对象前,必须要使用主键的setter方法给主键赋值,至于这个值怎么生成,完全由自己决定,这种方法应该尽量避免。
<id name="id" column="id">
<generator class="assigned" />
</id>
“ud”是自定义的策略名,人为起的名字,后面均用“ud”表示。
特点:可以跨数据库,人为控制主键生成,应尽量避免。
2、increment
由Hibernate从数据库中取出主键的最大值(每个session只取1次),以该值为基础,每次增量为1,在内存中生成主键,不依赖于底层的数据库,因此可以跨数据库。
<id name="id" column="id">
<generator class="increment" />
</id>
Hibernate调用org.hibernate.id.IncrementGenerator类里面的generate()方法,使用select max(idColumnName) from tableName语句获取主键最大值。该方法被声明成了synchronized,所以在一个独立的Java虚拟机内部是没有问题的,然而,在多个JVM同时并发访问数据库select max时就可能取出相同的值,再insert就会发生Dumplicate entry的错误。所以只能有一个Hibernate应用进程访问数据库,否则就可能产生主键冲突,所以不适合多进程并发更新数据库,适合单一进程访问数据库,不能用于群集环境。
官方文档:只有在没有其他进程往同一张表中插入数据时才能使用,在集群下不要使用。
特点:跨数据库,不适合多进程并发更新数据库,适合单一进程访问数据库,不能用于群集环境。
3、hilo
hilo(高低位方式high low)是hibernate中最常用的一种生成方式,需要一张额外的表保存hi的值。保存hi值的表至少有一条记录(只与第一条记录有关),否则会出现错误。可以跨数据库。
<id name="id" column="id">
<generator class="hilo">
<param name="table">hibernate_hilo</param>
<param name="column">next_hi</param>
<param name="max_lo">100</param>
</generator>
</id>
<param name="table">hibernate_hilo</param> 指定保存hi值的表名
<param name="column">next_hi</param> 指定保存hi值的列名
<param name="max_lo">100</param> 指定低位的最大值
也可以省略table和column配置,其默认的表为hibernate_unique_key,列为next_hi
<id name="id" column="id">
<generator class="hilo">
<param name="max_lo">100</param>
</generator>
</id>
hilo生成器生成主键的过程(以hibernate_unique_key表,next_hi列为例):
1. 获得hi值:读取并记录数据库的hibernate_unique_key表中next_hi字段的值,数据库中此字段值加1保存。
2. 获得lo值:从0到max_lo循环取值,差值为1,当值为max_lo值时,重新获取hi值,然后lo值继续从0到max_lo循环。
3. 根据公式 hi * (max_lo + 1) + lo计算生成主键值。
注意:当hi值是0的时候,那么第一个值不是0*(max_lo+1)+0=0,而是lo跳过0从1开始,直接是1、2、3……
那max_lo配置多大合适呢?
这要根据具体情况而定,如果系统一般不重启,而且需要用此表建立大量的主键,可以吧max_lo配置大一点,这样可以减少读取数据表的次数,提高效率;反之,如果服务器经常重启,可以吧max_lo配置小一点,可以避免每次重启主键之间的间隔太大,造成主键值主键不连贯。
特点:跨数据库,hilo算法生成的标志只能在一个数据库中保证唯一。
4、seqhilo
与hilo类似,通过hi/lo算法实现的主键生成机制,只是将hilo中的数据表换成了序列sequence,需要数据库中先创建sequence,适用于支持sequence的数据库,如Oracle。
<id name="id" column="id">
<generator class="seqhilo">
<param name="sequence">hibernate_seq</param>
<param name="max_lo">100</param>
</generator>
</id>
 
特点:与hilo类似,只能在支持序列的数据库中使用。
5、sequence
采用数据库提供的sequence机制生成主键,需要数据库支持sequence。如oralce、DB、SAP DB、PostgerSQL、McKoi中的sequence。MySQL这种不支持sequence的数据库则不行(可以使用identity)。
<generator class="sequence">
<param name="sequence">hibernate_id</param>
</generator>
<param name="sequence">hibernate_id</param> 指定sequence的名称
Hibernate生成主键时,查找sequence并赋给主键值,主键值由数据库生成,Hibernate不负责维护,使用时必须先创建一个sequence,如果不指定sequence名称,则使用Hibernate默认的sequence,名称为hibernate_sequence,前提要在数据库中创建该sequence。
特点:只能在支持序列的数据库中使用,如Oracle。
6、identity
identity由底层数据库生成标识符。identity是由数据库自己生成的,但这个主键必须设置为自增长,使用identity的前提条件是底层数据库支持自动增长字段类型,如DB2、SQL Server、MySQL、Sybase和HypersonicSQL等,Oracle这类没有自增字段的则不支持。
<id name="id" column="id">
<generator class="identity" />
</id>
例:如果使用MySQL数据库,则主键字段必须设置成auto_increment。
id int(11) primary key auto_increment
特点:只能用在支持自动增长的字段数据库中使用,如MySQL。
7、native
native由hibernate根据使用的数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式,灵活性很强。如果能支持identity则使用identity,如果支持sequence则使用sequence。
<id name="id" column="id">
<generator class="native" />
</id>
例如MySQL使用identity,Oracle使用sequence
注意:如果Hibernate自动选择sequence或者hilo,则所有的表的主键都会从Hibernate默认的sequence或hilo表中取。并且,有的数据库对于默认情况主键生成测试的支持,效率并不是很高。
使用sequence或hilo时,可以加入参数,指定sequence名称或hi值表名称等,如
<param name="sequence">hibernate_id</param>
特点:根据数据库自动选择,项目中如果用到多个数据库时,可以使用这种方式,使用时需要设置表的自增字段或建立序列,建立表等。
8、uuid
UUID:Universally Unique Identifier,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字,标准的UUID格式为:
xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12)
其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。
<id name="id" column="id">
<generator class="uuid" />
</id>
Hibernate在保存对象时,生成一个UUID字符串作为主键,保证了唯一性,但其并无任何业务逻辑意义,只能作为主键,唯一缺点长度较大,32位(Hibernate将UUID中间的“-”删除了)的字符串,占用存储空间大,但是有两个很重要的优点,Hibernate在维护主键时,不用去数据库查询,从而提高效率,而且它是跨数据库的,以后切换数据库极其方便。
特点:uuid长度大,占用空间大,跨数据库,不用访问数据库就生成主键值,所以效率高且能保证唯一性,移植非常方便,推荐使用。
9、guid
GUID:Globally Unique Identifier全球唯一标识符,也称作 UUID,是一个128位长的数字,用16进制表示。算法的核心思想是结合机器的网卡、当地时间、一个随即数来生成GUID。从理论上讲,如果一台机器每秒产生10000000个GUID,则可以保证(概率意义上)3240年不重复。
<id name="id" column="id">
<generator class="guid" />
</id>
Hibernate在维护主键时,先查询数据库,获得一个uuid字符串,该字符串就是主键值,该值唯一,缺点长度较大,支持数据库有限,优点同uuid,跨数据库,但是仍然需要访问数据库。
注意:长度因数据库不同而不同
MySQL中使用select uuid()语句获得的为36位(包含标准格式的“-”)
Oracle中,使用select rawtohex(sys_guid()) from dual语句获得的为32位(不包含“-”) 
特点:需要数据库支持查询uuid,生成时需要查询数据库,效率没有uuid高,推荐使用uuid。
10、foreign
使用另外一个相关联的对象的主键作为该对象主键。主要用于一对一关系中。
<id name="id" column="id">
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
<one-to-one name="user" class="domain.User" constrained="true" />
该例使用domain.User的主键作为本类映射的主键。
特点:很少使用,大多用在一对一关系中。
11、select
使用触发器生成主键,主要用于早期的数据库主键生成机制,能用到的地方非常少。
12、其他注释方式配置
注释方式与配置文件底层实现方式相同,只是配置的方式换成了注释方式
自动增长,适用于支持自增字段的数据库
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
根据底层数据库自动选择方式,需要底层数据库的设置
如MySQL,会使用自增字段,需要将主键设置成auto_increment。
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
使用表存储生成的主键,可以跨数据库。
每次需要主键值时,查询名为"hibernate_table"的表,查找主键列"gen_pk"值为"2"记录,得到这条记录的"gen_val"值,根据这个值,和allocationSize的值生成主键值。
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "ud")
@TableGenerator(name = "ud",
table = "hibernate_table",
pkColumnName = "gen_pk",
pkColumnValue = "2",
valueColumnName = "gen_val",
initialValue = 2,
allocationSize = 5)
使用序列存储主键值
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ud")
@SequenceGenerator(name = "ud",
sequenceName = "hibernate_seq",
allocationSize = 1,
initialValue = 2)


使用UUID生成主键
1.    @Id  
2.    @GenericGenerator(name="hibernate-uuid", strategy="uuid")  
3.    @GeneratedValue(Generator="hibernate-uuid")


4、关于hilo机制注意:
hilo算法生成的标志只能在一个数据库中保证唯一。
当用户为Hibernate自行提供连接,或者Hibernate通过JTA,从应用服务器的数据源获取数据库连接时,无法使用hilo,因为这不能保证hilo单独在新的数据库连接的事务中访问hi值表,这种情况,如果数据库支持序列,可以使用seqhilo。
5、使用identity、native、GenerationType.AUTO等方式生成主键时,只要用到自增字段,数据库表的字段必须设置成自动增加的,否则出错。
6、还有一些方法未列出来,例如uuid.hex,sequence-identity等,这些方法不是很常用,且已被其他方法代替,如uuid.hex,官方文档里建议不使用,而直接使用uuid方法。
7、Hibernate的各版本主键生成策略配置有略微差别,但实现基本相同。如,有的版本默认sequence不指定序列名,则使用名为hibernate_sequence的序列,有的版本则必须指定序列名。
8、还可以自定义主键生成策略,这里暂时不讨论,只讨论官方自带生成策略。 


3.常用注解
(1)类级别注解


@NamedNativeQuery:将SQL语句从程序中抽出来
name:指定命名SQL查询的名称 必须具备属性
query 指定命名SQL查询的查询语句 必须具备属性
resultClass 在简单查询中将查询结果映射成指定实体类的实例,类似于addEntity()作用。
非必须具备属性
resultSetMapping 该属性指定一个SQL结果映射的名称(该结果映射需要使用@SqlResultSetMapping定义),
用于使用该结果映射来转换查询结果集。
例子一:
// 定义一个命名SQL查询,其名称为simpleQuery  
@NamedNativeQuery(name="simpleQuery"  
   // 指定命名SQL查询对应的SQL语句  
   , query="select s.student_id , s.name from student_inf s"  
   // 指定将查询结果转换为Student实体  
   , resultClass=Student.class)  
@Entity  
@Table(name="student_inf")  
public class Student  {}


调用:List list = session.getNamedQuery("simpleQuery").list();


例子二:
// 定义一个命名SQL查询,其名称为queryTest  
@NamedNativeQuery(name="queryTest"  
   , query="select s.*,e.*,c.* from student_inf s,enrolment_inf e,"  
   + " course_inf c where s.student_id = e.student_id and"  
   + " e.course_code = c.course_code and e.year=:targetYear"  
   , resultSetMapping = "firstMapping") 
   // 指定使用名为firstMapping的@SqlResultSetMapping完成结果映射 
@SqlResultSetMapping(name="firstMapping"  
            ,entities={@EntityResult(entityClass=Student.class),  
                    @EntityResult(entityClass=Enrolment.class),  
                    @EntityResult(entityClass=Course.class,fields=  
                        {  
                                @FieldResult(name="id" , column="c.course_code"),  
                                @FieldResult(name="name" , column="c.name")  
                            }  
                      
                            )}  
          ,columns={@ColumnResult(name="s.name" ,type=String.class)}  
    )  


@NamedNativeQueries({}):当我们需要定义多个@NamedNativeQuery时,我们使用@NamedNativeQueries({})来管理


@SqlResultSetMapping:用于class上。例子上面。


@SqlResultSetMappings({}):我们需要定义多个@SqlResultSetMapping时,我们使用@SqlResultSetMappings({})管理


@Entity :声明该类为hibernate中的实体
属性值name表示该实体类在hibernate中的名称。例:
@Entity(name="teacher")//Teacher在hibernate中声明为teacher。默认为class的名称
@Table(name="t_teacher",indexes={@Index(columnList = "name"),@Index(columnList = "address")})
public class Teacher
//在使用时,sql/hql中用teacher来表示实体类
List<Teacher> one = session.createQuery("select new teacher(id,name,address) from teacher").list();
       


@Embeddable:这个注解也很好理解,这个是用来标明非实体类在实体类中的引用,常常跟@Embedded一起使用。
这里就要使用另外的两个注解@AttributeOverrides和@AttributeOverride,这两个注解是用来覆盖@Embeddable类中字段的属性将映射成的column。


@AttributeOverrides:里面只包含了@AttributeOverride类型数组; @AttributeOverride:包含要覆盖的@Embeddable类中字段名name和新增的@Column字段的属性;
使用如下:
Person类如下:


@Entity
public class Person implements Serializable{
   private static final long serialVersionUID = 8849870114127659929L;


   @Id
   @GeneratedValue
   private Long id;


   @Column(nullable = false)
   private String name;


   @Column(nullable = false)
   private Integer age;


   @Embedded
   @AttributeOverrides({@AttributeOverride(name="country", column=@Column(name = "person_country", length = 25, nullable = false)),
                       @AttributeOverride(name="city", column = @Column(name = "person_city", length = 15))})
   private Address address;


   //setter、getter
}
Address类如下:


@Embeddable
public class Address implements Serializable{
   private static final long serialVersionUID = 8849870114128959929L;


   @Column(nullable = false)
   private String country;
   @Column(length = 30)
   private String province;
   @Column(unique = true)
   private String city;
   @Column(length = 50)
   private String detail;
   //setter、getter
}


@Table :实体与表的关系
属性:
name:表示该实体对应的数据库中的表。
catalog:数据库的名称。
schame:数据库的名称。Mysql中catalog/schame只能存在一个。
uniqueConstraints:表的唯一约束
如果是联合约束就用下面这种
@Table(name="tbl_sky",
  uniqueConstraints = {@UniqueConstraint(columnNames={"month", "day"})})
如果是单一字段约束可以用
@Table(name="tbl_sky",
  uniqueConstraints = {@UniqueConstraint(columnNames="month")})
indexes:表中的索引. @Index为javax.persistence.Index;
如果是联合索引就用下面这种
@Table(name="tbl_sky",
  indexes = {@Index(columnNames={"month", "day"})})
如果是单一字段索引可以用
@Table(name="tbl_sky",
  indexes={@Index(columnList = "name"),@Index(columnList = "address")})




@SecondaryTable:一个entity class可以映射到多表,SecondaryTable用来定义单个从表的名字,主键名字等属性。
元数据属性说明:
name: 同@table属性
catalog: 同@table属性
schema:同@table属性
pkJoinColumns: 定义一个PrimaryKeyJoinColumn数组,指定从表的主键列
UniqueConstraints:同@table属性
indexes:同@table属性
foreignKey:----
下面的代码说明Customer类映射到两个表,主表名是CUSTOMER,从表名是CUST_DETAIL,从表的主键列和
主表的主键列类型相同,列名为CUST_ID。
   @Entity
   @Table(name="CUSTOMER")
@SecondaryTable(name="CUST_DETAIL",pkJoinColumns=@PrimaryKeyJoinColumn(name="CUST_ID"))
public class Customer { ... }
@SecondaryTables:当一个entity class映射到一个主表和多个从表时,用SecondaryTables来定义各个从表的属性。
value:public abstract SecondaryTable[] value();用来声明多个SecondaryTable


@Table(name = "CUSTOMER")
@SecondaryTables( value = {
@SecondaryTable(name = "CUST_NAME", pkJoinColumns = { @PrimaryKeyJoinColumn(name =
"STMO_ID", referencedColumnName = "id") }),
@SecondaryTable(name = "CUST_ADDRESS", pkJoinColumns = { @PrimaryKeyJoinColumn(name =
"STMO_ID", referencedColumnName = "id") }) })
public class Customer {}




(2)属性级别
@Id 
@GeneratedValue @GenericGenerator 

@Column :元数据定义了映射到数据库的列
name:列名。
unique: 是否唯一
nullable: 是否允许为空
insertable: 是否允许插入
updatable: 是否允许更新
columnDefinition: 定义建表时创建此列的DDL
table: 从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字

int length():长度
precision:表示数字中的有效位。如果没有指定precision的话,Oracle将使用38作为精度。 
scale:表示数字小数点右边的位数,scale默认设置为0.  如果把scale设成负数,Oracle将把该数字取舍到小数点左边的指定位数。
       
   public class Person {
@Column(name = "PERSONNAME", unique = true, nullable = false, updatable = true)
private String name;
@Column(name = "PHOTO", columnDefinition = "BLOB NOT NULL",
table="PER_PHOTO")
private byte[] picture;


@Emebedded @EmbeddedId //与@Emebeddable一起使用


@Version :属性映射到 "OPTLOCK" 列,entity manager 使用这个字段来检测冲突。一般可以用数字或者 timestamp 类型来支持 version.
int类型:
@Version
   @Column(name = "version",length = 11)
   private int getVersion()
时间:
@Version
   @Temporal(TemporalType.TIMESTAMP)
   @Column(name = "lastDate")
   public Date getLastDate()


@Lob :注解属性将被持久化为 Blog 或 Clob 类型。具体的java.sql.Clob, Character[], char[] 和 java.lang.String 将被持久化为 Clob 类型. java.sql.Blob,Byte[], byte[] 和 serializable type 将被持久化为 Blob 类型。
@Lob
public String getFullText() {
  return fullText;  // clob type
}


@Lob
public byte[] getFullCode() {
 return fullCode;  // blog type
}


@Temporal(TemporalType.DATE,TIME,TIMESTAMP)分别Map java.sql.(Date,Time, Timestamp). 
声明列为时间或日期字段。


@Transient //隐藏属性,不会被映射到表中


@Enumerated:表示持久化的列是枚举类型,STRING表示持久化的为枚举类型的名称。默认为ORDINAL,持久化值。
例如标注的为STRING,这样持久化实体后,数据库中保存的是枚举类型的名称. 
例如标注的为ORDINAL,这样持久化实体后,数据库中保存的是枚举类型的顺序值. 


@Inheritance:继承映射。需要使用 strategy 属性指定继承策略,继承策略有 SINGLE_TABLE、TABLE_PER_CLASS 和 JOINED 三种。
strategy:INGLE_TABLE、TABLE_PER_CLASS 和 JOINED


SINGLE_TABLE:将父类和其所有的子类集合在一块,存在一张表中,并创建一个新的字段来判断对象的类型。@DiscriminatorColumn:表中判断对象类型的列,@DiscriminatorValue:判断对象的值
@Entity
  @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
  @DiscriminatorColumn(name="dis",discriminatorType=DiscriminatorType.STRING)
  @DiscriminatorValue("person")//discriminator列 的值为person时,表明这条记录为person对象
  public class Person {
  @Id
  @GeneratedValue
  private int id;
  private String name;
  //Getters and setters omitted here...
}
@Entity
  @DiscriminatorValue("student")
  public class Student extends Person{
  private int score;
  //Getters and setters omitted here...
  }
  生成的表:person(dis、id、name、score)


  TABLE_PER_CLASS:TABLE_PER_CLASS 是为每一个类创建一个表,这些表是相互独立的。
  Person.java:
  1:  @Entity
2:  @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
3:  public class Person {
4:      @Id
5:      private int id;
6:      private String name;
7:  //Getters and setters omitted here...
8:  }
Student.java:
1:  @Entity
2:  public class Student extends Person{
3:      private int score;
4:  //Getters and setters omitted here...
5:  }
生成的表:person(id、name) student(id、name、score)
JOINED 是将父类、子类分别存放在不同的表中,并且建立相应的外键,以确定相互之间的关系。
Person.java:
1:  @Entity
2:  @Inheritance(strategy=InheritanceType.JOINED)
3:  public class Person {
4:      @Id
5:      @GeneratedValue
6:      private int id;
7:      private String name;
8:  //Getters and setters omitted here...
9:  }
子类中只需声明与父类不同的域即可:
Student.java:
1:  @Entity
2:  public class Student extends Person{
3:      private int coat;
4:  //Getters and setters omitted here...
5:  }
生成的表:person(id、name);student(id、coat);student的id是person的id




@Entity:很明显,这个是代表注册实体类的,就是我们建立的符合javaBean规范的实体类。
@Table:这个也很容易理解,这个是对应实体类在数据库表的声明,@Table(name="要生成的表名",schema="数据库",catalog="目录(mysql不支持,所以一般去掉)")
!!!@Entity跟@Table这两个实体类的注解一般是一起使用的


@EmbeddedId:用来标明联合主键的,跟@Embeddable一起使用
@Id:标明主键
@GeneratedValue:主键策略生成器,属性:(generator=”主键的名字(只常用这一个)“,strategy=GenerationType.AUTO(自动生成,一般不用到))
@@GenericGenerator :主键生成策略,属性有(name="上面主键的名字",strategy="主键生成的策略assigned等")


4.扫描注解类
<mapping class="com.test.entity.mtm.Course"/>


5.初始化hibernate容器
private  static Configuration configuration = new Configuration();    
   private static ServiceRegistry serviceRegistry;
   private static String configFile = CONFIG_FILE_LOCATION;


   static {
       try {
           /*
            * Hibernate4.3 已经放弃用该方式注册sessionFactory,
            * 可以用如下方式获取。
            */
           configuration.configure(configFile);  
           serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build(); 
           sessionFactory = configuration.buildSessionFactory(serviceRegistry);
           
       } catch (Exception e) {
           System.err.println("##############################Error Creating SessionFactory##############################");
           e.printStackTrace();
       }
   }


6.事务管理

事务的特性ACID


    A(atomicity):原子性:事务不可被划分,是一个整体,要么一起成功,要么一起失败


    C(consistence):一致性,A转100给B,A减少了100,那么B就要增加100,增加减少100就是一致的意思


    I(isolation):隔离性,多个事务对同一内容的并发操作。


    D(durability):持久性,已经提交的事务,就已经保存到数据库中。


脏读、不可重复读、幻读
脏读:事务1读取事务2没有提交的数据,事务1读到的数据是没有意义的数据。


不可重复读:事务1读取数据后,事务2修改了数据,事务1读取的数据是过时的数据。


幻读:事务1读取数据后,事务2添加了数据,事务1读取的数据是部分数据。


事务隔离级别,用于解决隔离问题:
read uncommitted :读未提交,一个事务 读到 另一个事务 没有提交的数据,
存在问题3个,解决0个问题。


read committed:读已提交,一个事务 读到 另一个事务 已经提交的数据,
存在问题2个,解决1个问题(脏读问题)。Oracle默认隔离级别


repeatable read:为读取的数据加锁(不允许其他事务更改读取的数据)。
存在问题1个,解决2个问题(脏读、不可重复读)。mysql默认隔离级别


serializable:同时只有一个事务可以操作,其他事务挂起(暂停),
存在问题0个,解决3个问题(脏读、不可重复读、虚读)


hibernate事务配置:
在hiberante.cfg.xml文件中配置 hibernate.connection.isolation 隔离级别


有四种隔离级别可选择,后面的数字表示在设置隔离级别的时候,直接写数字也是可以代表对应的隔离级别的,


      Read uncommoitted isolation  1


      Read committed isolation    2


    Repeatable read isolation   4


    Serializable isolation     8


在配置文件中的SessionFactory设置
<property name="hibernate.connection.isolation">4</property>


丢失更新问题 lost update
最后更新数据,将前面更新的数据给覆盖了


解决:
乐观锁:
在User.hbm.xml 定义版本字段:<version name="version"></version>或者@Version
悲观锁:
Query.setLockOptions(LockOptions.NONE/READ/UPGRADE/NO_WAIT/WAIT_FOREVER/SKIP_LOCKED)


session.get(paramClass, paramSerializable, LockOptions.NONE/READ/UPGRADE/NO_WAIT/WAIT_FOREVER/SKIP_LOCKED)


session.load(paramClass, paramSerializable, LockOptions.NONE/READ/UPGRADE/NO_WAIT/WAIT_FOREVER/SKIP_LOCKED)





事务传播属性:
事务传播行为可以由事务属性propagation指定,Spring定义了7种传播行为,这些行为在org.springframework.transaction.TransactionDefinition接口中定义。
REQUIRED:如果现有的事务正在进行,当前方法应该在这个事务中运行,否则,它应该启动新事务,并在自己的事务中运行。
REQUIRES_NEW:当前方法必须启动新事物,并在自己的事务中运行,如果现有的事务正在进行,它应该挂起。
SUPPORTS:如果现有事务正在进行,当前方法应该运行在该事务中,否则,它没有必要运行在事务中。
NOT_SUPPORTED:当前方法不应该运行在事务中。如果现有事务正在运行,它应该挂起。
MANDATORY:当前方法必须运行于一个事务中,如果没有事务在进行中,将抛出一个异常。
NEVER:当前方法不应该运行于事务中,如果现有事务在运行中,将抛出异常。
NESTED:如果现有事务正在运行,当前方法应该运行在嵌套的事务中,否则它应该启动一个新事务并运行在自己的事务中,这种功能是Spring特有的。该行为对于批处理等情况有用,在这种情况下,你需要处理很长的处理过程,而你希望在批处理中每次提交大量的记录,所以你每隔10000个记录提交一次,如果出现某种错误,你回滚嵌套的事务,仅仅丢失10000条记录。

配置:
第一种:tx模式声明模式
<context:annotation-config />  
   <context:component-scan base-package="com.bluesky" />  
 
   <bean id="sessionFactory"    
           class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">    
       <property name="configLocation" value="classpath:hibernate.cfg.xml" />    
       <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />  
   </bean>    
 
   <!-- 定义事务管理器(声明式的事务) -->    
   <bean id="transactionManager"  
       class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
       <property name="sessionFactory" ref="sessionFactory" />  
   </bean>  
 
   <tx:advice id="txAdvice" transaction-manager="transactionManager">  
       <tx:attributes>  
           <tx:method name="*" propagation="REQUIRED" />  
       </tx:attributes>  
   </tx:advice>  
     
   <aop:config>  
       <aop:pointcut id="interceptorPointCuts"  
           expression="execution(* com.bluesky.spring.dao.*.*(..))" />  
       <aop:advisor advice-ref="txAdvice"  
           pointcut-ref="interceptorPointCuts" />          
   </aop:config>        
   第二种:tx注解模式
   <context:annotation-config />  
   <context:component-scan base-package="com.bluesky" />  
   <bean id="sessionFactory"    
           class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">    
       <property name="configLocation" value="classpath:hibernate.cfg.xml" />    
       <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />  
   </bean>    
 
   <!-- 定义事务管理器(声明式的事务) -->    
   <bean id="transactionManager"  
       class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
       <property name="sessionFactory" ref="sessionFactory" />  
   </bean>  
   <tx:annotation-driven transaction-manager="transactionManager"/> 


   @Transactional(value=TxType.REQUIRED/REQUIRES_NEW/MANDATORY/SUPPORTS/NOT_SUPPORTED, NEVER;,rollbackOn={xxerr.class,..},dontRollbackOn={xxerr.class,..}) 
@Component("userDao")  
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {  
//会覆盖class上,声明的事务属性。
  @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
   public List<User> listUsers() {  
       return this.getSession().createQuery("from User").list();  
   }  
     
      


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值