Hibernate框架笔记

来源:互联网 发布:日韩男士帽子 知乎 编辑:程序博客网 时间:2024/06/07 04:24

Hibernate

课程内容:

1.   常见的O/R框架(了解)

2.   Hibernate基础配置(重点)

3.   ID生成策略(重点掌握auto)

4.   Hibernate 核心开发接口介绍(重点)

5.   对象的三种状态(了解)

6.   关系映射(重点)

7.   HQL(了解或重点)

8.   性能优化(重点)

9.     

一.hibernate操作步骤

  1. 可以将常用的jre包整合起来下次使用直接调用

1.1右击项目àbuild pathàconfigurebuildpathàlibrariesà

1.2addlibraryàuser libraryàuser librariesànew à取个名字àadd jars—》加入需要的jar包

1.3  a. hibernate3.jar

          b. lib\required 目录下的jar 包

          c.slf4j-nop-1.5.8.jar

          d.  数据库驱动jar包

1.4建立hibernate配置文件 默认名字为: hibernate.cfg.xml(src下面创建)

     a.从参考文档中copy(文件头别忘了)

    b.修改对应的数据库连接

    c.注释暂时用不上的内容

1.5创建持久化类

    a.对数据库dept表进行操作

    b.创建包com.whhp.hibernate.mode

c. 创建实体类 dept

   1.6创建dept类的映射文 Dept.hbm.xml(在该类的包下面创建)

    a.参考文档copy(文件头别忘了)

    b.id元素中填写主键

c. generator取值范围"native"由Hibernate根据使用的数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式。

d.字段名与列名相同,列名可以省略不写(表名一样)

  1.7将映射文件加入到配置文件hibernate.cfg.xml中.

      a.参考文档

      b. <mappingresource="com/whhp/hibernate/mode/Dept.hbm.xml"/>

  1.8编写测试类 Main 在Main中对Dept表进行操作

     a.参考文档

 

二.方法2  通过myeclipse配置生成连接数据库 和配置文件

连接数据库

1. windows==》工具栏—》Open perspective àmyeclipse  database  explorerànew

2.Oracle(Thin driver)

3. driver name:orcl(数据库名)

Connection URl: jdbc:oracle:thin:@127.0.0.1:1521:orcl

4. 保存密码 测试连接

5. 选择 display theselected schemas

6.添加用户

7. 右击打开连接 查看该用户下面的表

生成配置文件

1. 右击项目,选择myeclipse,选择 add hibernate  capabilities

2. 选择hibernate 3.3core(如果使用自己的jar就勾上 User libraries)

3. Nextànext create sessionfactory class 钩去掉

编写sql

1. 右击项目创建文件夹sql

2. 创建文件dept.sql

3. 选择操作的数据库

4. 编写sql语句

编写映射文件

三.实现对数据库的增删改查

1. 不再创建Main 方法 使用junit来完成

a)  在项目下面创建文件夹(sourceFolder) 取名为test

b)  在文件夹中创建包,包名与需要测试的类所在的包名一致(避免导包)

c)  创建juit测试类 名字为 类名+test

d)  Browse(添加要测试的类) à  Next à添加要测试的方法

e)  在方法中创建对象调用要测试的方法,检查是否成功

四.Annotation版 helloworld

 Hibernate 3.0开始支持Annotation

a)  添加jar包  hibernate-annotations.jar   ejb3-persistence.jar    hibernate-commons-annotations-3.3.0.ga.jar

b)  实体类添加注解 类名添加@entity 主键添加@id  (注意: import  javax.persistence)无需创建实体类映射了

c)   Hinernate.cfg.xml 写法不一样  <mappingclass="cn.whhp.mode.Teacher" />

d)  Annotation的对象不一样了例如:

 Configuration configuration=newAnnotationConfiguration();

                       Configurationconfiguration2=configuration.configure();

                         SessionFactory sFactory=configuration2.buildSessionFactory();

                          Session session=  sFactory.getCurrentSession();

                          session.beginTransaction();

                          Teacher teacher=new Teacher();

                          //teacher.setId(3);

                          teacher.setName("习大大");

                          teacher.setTitle("主席");

                         session.save(teacher);

                         session.getTransaction().commit();

. O/RMapping   Obejct/Relation Mapping 对象关系映射

1.  jdbc操作数据很繁琐

2.  sql语言编写并不是面向对象

3.  可以在对象和关系表之间建立关联来简化编程

4.  O/R Mapping 简化编程

5.  O/R Mapping跨越数据库平台 

六 O/R Mapping Frameworks

1.  Hibernate

2.  Toplink(Java对象关系可持续性体系结构,优秀的对象关系映射持久层解决方案)

3.  Jdo(Java对象持久化的新的规范,也是一个用于存取某种数据仓库中的对象的标准化API)

4.  ibatis

5.     JPA(JPA(JavaPersistence API)Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关系映射工具来管理Java应用中的关系数据。,而Hibernate是它的一种实现。除了Hibernate,还有EclipseLink(曾经的toplink)OpenJPA等可;供选择,所以使用Jpa的一个好处是,可以更换实现而不必改动太多代码)  意愿:一统天下

七hibernate 基础配置

1.  Hibernate.cfg.xml:hbm2ddl.auto(用的比较多是create

a)     Create 自动在数据库创建表

b)    Update 根据实体类更新表结构

c)     create-drop关闭SessionFactory会把数据库创建好的表给删掉

d)    validate 对数据操作之前会检查表的结构是否与配置文件是否匹配

2.先建表还是先建类?

3.搭建日志环境并配置显示DDL语句

a)     问题?控制台只显示插入的sql语句未显示建表sql

b)    日志框架 slf

c)     Hibernate 3.3.2所使用的slf接口定义了一系列规范

d)    具体的实现有很多种1.slf4j 2.log4j 3.jdk logging api 4.appche commons-logging

e)    用的较多log4j 可以用slf接口 log4j来实现

步骤:

                i.         添加slf4j-log4j12-1.5.5.jar包实现 slf4jlog4j之间的对接

               ii.         添加log4jjar log4j-1.2.15.jar

              iii.         编写Log4j配件文件API中复制)路径hibernate-distribution-3.3.2.GA\project\etc

             iv.         log4j.logger.org.hibernate.tool.hbm2ddl=debug可以在控制台把建表以及其他相关信息打印出来

4.hibernate.cfg.xml: show_sql   输出所有SQL语句到控制台

5.hibernate.cfg.xml:format_sqllogconsole中打印出更漂亮的SQL

 

6.表名和类名不同,对表名进行配置

a)     Annotation:@Table(name=”表名”)

b)    Xml:查询参考文档

7.字段名和属性相同

a)     Annotation:默认为@Basic,写不写都可以

b)    Xml中不用写column

8.字段名和属性不同

a)     Annotation:@Column( name=”列名”)(写在字段get方法上)

b)    Xml: 询参考文档

9.不需要persistence的字段

a)     Annotation:@Transient

b)    Xml不写

10.映射日期与时间类型,指定时间精度

a)      Annotation:@Temporal  @Temporal(TemporalType.TIMESTAMP)

b)    Xml:指定Type

11.映射枚举类型

a)     Annotation:

@Enumerated(EnumType.STRING)字符串枚举类型

@Enumerated(EnumType.ORDINAL)数序(数字)枚举类型(数据库存枚举的下标)

b)    Xml:相当麻烦

12.@Lob

13.CLOBBLOB类型的数据存取

14.Hibernate自定义数据类型

15.hibernate类型

1、Java基本类型的Hibernate映射类型

Hibernate映射类型

Java类型

标准SQL类型

大小和取值范围

integer或者int

int或者java.lang.Integer

INTEGER

4字节

long

long Long

BIGINT

8字节

short

short Short

SMALLINT

2字节

byte

byte Byte

TINYINT

1字节

float

float Float

FLOAT

4字节

double

double Double

DOUBLE

8字节

big_decimal

java.math.BigDecimal

NUMERIC

NUMERIC(8,2)8

character

char Character String

CHAR(1)

定长字符

string

String

VARCHAR

变长字符串

boolean

boolean Boolean

BIT

布尔类型

yes_no

boolean Boolean

CHAR(1) (Y-N)

布尔类型

true_false

boolean Boolean

CHAR(1) (T-F)

布尔类型

 2、Java时间和日期类型的Hibernate映射

映射类型

Java类型

标准SQL类型

描述

date

util.Date或者sql.Date

DATE

YYYY-MM-DD

time

Date    Time

TIME

HH:MM:SS

timestamp

Date   Timestamp

TIMESTAMP

YYYYMMDDHHMMSS

calendar

calendar

TIMESTAMP

YYYYMMDDHHMMSS

calendar_date

calendar

DATE

YYYY-MM-DD

3、Java大对象类型的Hibernate映射类型

映射类型

Java类型

标准SQL类型

MySQL类型

Oracle类型

binary

byte[]

VARBINARY(BLOB)

BLOB

BLOB

text

String

CLOB

TEXT

CLOB

serializable

Serializable接口任意实现类

VARBINARY(BLOB)

BLOB

BLOB

clob

java.sql.Clob

CLOB

TEXT

CLOB

blob

java.sql.Blob

BLOB

BLOB

BLOB

 

考虑到 操作 blob的字段太复杂一个变换的技巧是 .实体类用 byte[]类型 ,  hibernate类型用 binary ,数据库还是用 blob .这样可以简化一些操作

八.ID生成策略

1.      Hibernate生成表的结构并不是为了将来就用它生成,可能还有自己的扩展,比如index,而是为了明白我们应该建立什么样的表和实体映射。

2.      Xml生成id

generator

常用四个:native identity sequence uuid(数据库跨平台选native uuid)

3.      @GeneratedValue参考annotation API

1. 手动指定;没说的之前写过,xml和属性都使用过;@Id

2. 使用 Generator生成器;

Generator 有很多值:常用的有native , identity, sequence,uuidid类型 String ;

使用Generarot帮我们自动生成ID

@GeneratedValue

注解在 @Id下:

@Id

@GeneratedValue

默认策略 auto/native;如果你使用MySQL,那么自动使用 auto_increment

指定ID生成策略@GeneratedValue

@GeneratedValue(strategy=GenerationType.AUTO)@GeneratedValue(strategy=GenerationType.IDENTITY) @GeneratedValue(strategy=GenerationType.SEQUENCE)@GeneratedValue(strategy=GenerationType.TABLE)

这里要注意要使用数据库支持的,比如这里MySQL我们使用IDENTITY

a)      自定义ID

b)      AUTO(Teacher2)

默认:Oracle使用hibernate_sequence

MySql 使用 auto_increment

c)      Identity   @GeneratedValue(strategy=GenerationType.IDENTITY)只能用在MySqlSqlServer,Oracle不能用

d)      SEQUENCE  @GeneratedValue(strategy=GenerationType.SEQUENCE)只能用在Oracle

                i.         默认使用的序列名字为hibernate_sequence,如果需要指明序列可以如下操作

               ii.         类名前加上@SequenceGenerator(name="teacherSEQ",sequenceName="seq_teacher")

              iii.         ID前加上@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="teacherSEQ")

             iv.         首先会查找在数据库中是否存在序列seq_teacher,不存在创建

e)      @ TableGenerator 通过表来生成id(参考Annotation  API)(跨数据库平台时可以用到,一般很少用)

在类前面加上@TableGenerator(

     name="Teacher_GEN",(给表取个别名,方便在使用时查找)

     table="GENERATOR_TABLE",(表本身的名字)

      pkColumnName ="key",(第一列的列名)

      valueColumnName ="hi",(第二列的列名)

     pkColumnValue="EMP",(表中一条记录第一列的值为EMP

      allocationSize=1(每次取值后加1

       )

     类的属性ID前面做如下修改@Id

  @GeneratedValue(strategy=GenerationType.TABLE,generator="Teacher_GEN")

  public int getId() {

         return id;

       }

f)       联合主键(能用1个就不要用2)

a)      Xml:

                         i.         要重写equalshashCode保证主键的唯一性

                        ii.         要实现serializable数据转移时需要

                       iii.         现将idname作为主键

Student表中    private StudentPK pk;

创建主键表StudentPK,idname属性 实现Serializable,重写2个方法

@Override

  public boolean equals(Object o){

    if (o instanceof StudentPK){

         StudentPK sPk=(StudentPK) o;

         if (this.id==sPk.id&&sPk.name.equals(this.name)) {

                return true;

         }

    }

                return false;

  }

  @Override

  public int hashCode(){

         return this.name.hashCode();

       }

Student映射文件主键写法

<composite-idname="pk" class="cn.whhp.model.StudentPK">

                <key-property name="id" />

                <key-property name="name" />

              </composite-id>

b)      Annotation

xml

在主表中添加注解

@EmbeddedId

  public StudentPK getPk() {

         return pk;

       }

九.FAQ

Junit测试时HibernateSessionFactory初始化异常不提示下(junitbug),解决如

                         i.         Juint测试方法中添加try cath

                        ii.         main方法测试

 

 

 

十.核心开发接口介绍

Hibernate 核心接口 三种对象状态 四种操作方法

a)      Configuration

                         i.         AnnotationConfiguration

                        ii.         进行配置信息管理

                       iii.         用来产生SessionFactory

                       iv.         可以再configure方法在指定hibernate配置文件

                        v.         只需要关注一个方法:buildSessionFactory

b)      SessionFactory

                         i.         用来产生和管理session

                        ii.         通常情况下每个应用只需要一个SessionFactory

                       iii.         除非要访问多个数据库的情况

                       iv.         关注两个方法即可:openSesion getCurrentSession

Opensession每次都是新的

getCurrentSession从上下文找,如果有,用旧的,如果没有,建新的

c)      Session

管理一个数据库的任务单元

方法

a)      Save()

b)      Delete()

c)      Update()

d)      SaveOrUpdate()

save()方法很显然是执行保存操作的,如果是对一个new出来的对象进行保存,自然要使用这个方法了,数据库中没有这个对象。

update()如果是对一个已经存在的游离对象进行更新那么肯定是要使用update()方法了,数据中有这个对象。

saveOrUpdate()这个方法是更新或者插入,有主键就执行更新,如果没有主键就执行插入。

区别:对于一个从托管状态到瞬态的对象(对于一个从数据库中取出来又被删除的对象),这个对象本身是有主键的,但是因为被删除了,所以这个时候因为数据库中已经没有了这条记录了。不过它还有主键存在,所以这个时候不可以使用update()或者是saveOrUpdate(),因为update()方法是认为数据库中肯定有这条记录的,而saveOrUpdate的执行过程就是先查看这个对象是不是有主键,有主键那么就执行update()方法,没有主键就执行save()方法,因此结果跟调用了update()方法的效果是一样的,结果就会出错,因为这个对象已经被删除了,数据库中已经没有这条记录了,只是它还有主键而已(仅仅是存在于内存中),因此这个时候要执行的是save()方法

 

e)      Load

f)       Get

g)      Getload的区别

get方法首先查询session缓存,没有的话直接发送sql查询数据库,一定要获取到真实的数据,否则返回null,并不适用二级缓存;反而load方法创建时首先查询session缓存,没有就创建代理,实际使用数据时才查询二级缓存和数据库,hibernate对于load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,就抛异常所谓延迟加载也称为懒加载就是当在真正需要数据的时候,才真正执行数据加载操作。可以简单理解为,只有在使用的时候,才会发出sql语句进行查询。

代理实际就是空的对象 并没有去数据库查询得到的我们叫代理对象,如果 去数据库查询了返回到了这个对象 我们叫实体对象就是这个对象真实存在

h)      Clear方法

无论是load还是get都会首先查找缓存(一级缓存),如果没有,才会从数据库查找,调用clear()方法可以强制清除session缓存

Session.Commit() 这个方法它自动调用了close()flush()方法

i)        调用flush()可以强制从内存到数据库的同步

三种对象状态图

j)       三种状态的区别
区分方法:有没有id,数据库中有没有,缓存中有没有
Trasient(
临时状态):没有id    数据库中没有,  缓存没有
Persistent(
持久化状态):有id,  数据库有,      缓存有
Detached(
游离状态):有id     数据库有,       缓存没有

Configuration configuration=new AnnotationConfiguration();

              Configurationconfiguration2=configuration.configure();

              SessionFactorysFactory=configuration2.buildSessionFactory();

              Sessionsession=sFactory.getCurrentSession();

              session.beginTransaction();

              //临时状态

              Peoplepeople=new People();

              people.setName("女娲");

              people.setSex(Gender.male);

              //持久状态

              session.save(people);

              session.getTransaction().commit();

              //游离状态

              System.out.println(people.getId());缓存:

 

save方法完成之后,session将对象引用放在session缓存区中,

 有一个hasMap引用,里面有一系列key和对应的vaule指向这个应用的对象。

 比如上面的save(t)完成之后,会有一个idkey,然后value指向数据库中那个对象。

 

 

 

 

 

增删查改:

Configuration configuration=new AnnotationConfiguration();

Configuration configuration2=configuration.configure();

SessionFactory sFactory=configuration2.buildSessionFactory();

       /**

        * 新增

        */

       public voidsavePeople(){

              Sessionsession=sFactory.getCurrentSession();

              session.beginTransaction();

              //临时状态

              Peoplepeople=new People();

              people.setName("耶稣");

              people.setSex(Gender.male);

              //持久状态

              session.save(people);

              session.getTransaction().commit();

              //游离状态

              System.out.println(people.getId());

       }

      

       public voidloadPeople(){

              Sessionsession=sFactory.getCurrentSession();

              session.beginTransaction();

              Peoplepeople=(People) session.load(People.class,2);

              System.out.println("load方法加载之前"+people.getName());

              session.getTransaction().commit();

              System.out.println("load方法加载之后"+people.getName());

              System.out.println(people.getId());

             

       }

       public  void getPeople(){

              Sessionsession=sFactory.getCurrentSession();

              session.beginTransaction();

              Peoplepeople=(People) session.get(People.class, 2);

              System.out.println("get方法加载之前"+people.getName());

              session.getTransaction().commit();

              System.out.println("get方法加载之后"+people.getName());

              System.out.println(people.getId());

       }

      

       public voiddeletePeople(){

              //必须有id好才能delete,也就是新new的对象不能deletesave之后才能提交

              //id是对象的唯一标识,不管你姓名和别的参数怎么设置都会按照id进行删除,

              //比如下面的name属性就不必设置了,但设置成别的参数也没有问题

              Sessionsession=sFactory.getCurrentSession();

              session.beginTransaction();

              Peoplepeople=new People();

              people.setId(1);

              session.delete(people);

              session.getTransaction().commit();

              System.out.println(people.getId());

       }

      

       public voidupdatePeople(){

              Sessionsession=sFactory.getCurrentSession();

              session.beginTransaction();

              Peoplepeople=(People) session.get(People.class,3);

              session.getTransaction().commit();

              Sessionsession1=sFactory.getCurrentSession();

              session1.beginTransaction();//people的状态为游离

              people.setName("Updatename");

              session1.update(people);

              session1.getTransaction().commit();

              System.out.println("更新后名字为:"+people.getName());

             

       }

      

       public voidclearPeople(){

              Sessionsession=sFactory.getCurrentSession();

              session.beginTransaction();

              Peoplepeople=(People) session.load(People.class,3);

              System.out.println(people.getName());

              //如果不用这个clear方法查找同一个id只发出一条语句到数据库,

              //因为在缓存中能找到,如果用clear清楚缓存之后,就会重新到数据库查询

              session.clear();

              Peoplepeople1=(People) session.load(People.class,3);

              System.out.println(people1.getName());     

              session.getTransaction().commit();

       }

      

       public voidtestflush() {

              Sessionsession=sFactory.getCurrentSession();

              session.beginTransaction();

              Peoplepeople=(People) session.load(People.class,3);

              System.out.println(people.getName());

              people.setName("NameA");

              //不加flush只会在commit的时候进行提交,两个set提交一次,

              //最后设置的setter内容;当flush的时候会立即提交

              session.flush();

              people.setName("NAMEB");

              session.getTransaction().commit();

       }

十一.关系映射

对象之间的关系 

1.  这里的关系映射指的是对象之间的关系,并不是指数据库的关系,而是当对象之间处于下列关系之一时,数据库表该如何映射,编程上该如何处理

a)   一对一:单向(主键,外键),双向(主键,外键)

b)   一对多:单向,双向(和多对一双向相同)

c)   多对一:单向,双向(一对多双向和多对一双向是一样的)

d)   多对多:单向,双向

e)   (一对一单/双向主键关联映射,只作了解)

f)   集合映射:list ,map,set

g)   继承映射(了解):单表,多表,一张主表多张子表

h)   组件映射:@Embeddable,@Embedded

 

一对一(one to one)单向关联映射

两个对象是一对一的的关系.

有两种策略可以实现一对一的关联映射

主键关联:即让两个对象具有相同的主键值,以表明他们之间的一对一的对应关系;数据库表不会有额外的字段来维护他们之间的关系,仅通过表的主键关系来维护.一对一主键关联映射默认了级联属性,其关联对象会同时存储.所以不会抛出TransientObjectException异常.

唯一外键关联:外键关联,本来是用于多对一的配置,但是如果加上唯一的限制之后,也可以表示一对一的关联关系.unique="true".

单向关联,如Person-person_id;加载person信息时能关联对应的person_id信息

双向关系,加载任何一方,都能关联出别一方的信息.

注意id的主键生成策略,foreign使用另外一个相关联的对象的标识符。通常和<one-to-one>联合起来使用。

类Person(id,name,idCard),

类IdCard(id,cardNo)

一对一(单向)基于主键关联映射(了解)

XML配置方法

一对一单向主键关联通常使用一个特定的id生成器。

<classname="Person" table=”t_person”>

    <id name="id" >

        <generatorclass="foreign">

            <param name="property">idCard</param>

        </generator>

    </id>

    <one-to-one name="idCard"constrained="true"/>

</class>

one-to-one不会加载字段,它告诉HIBERNATE怎样加载其引用对象.如何加载呢,默认根据主键加载其引用对象.如在t_person中查到id=2,自动加载t_idCard中id=2的对象信息.constrained="true",表明person主键是个外键,表示当前主键上存在着idCard约束,当前主键id作为外键,参照了idCard.

<param name="property">idCard</param>表明person中的id来源于idCard,也就是共享idCard的主键.

Annotation配置一对一(单向)主键关联映射.(BUG)

@OneToOne

@PrimaryKeyJoinColumn

BUG,系统不会生成主键映射.推荐使用XML配置方法.

 

一对一(单向)基于外键关联映射

和单向多对一关联几乎是一样的。唯一不同的就是单向一对一关联中的外键字段具有唯一性约束。这种方法会在表中生成一个新的外键字段.如果不限制外字段的唯一性约束,就会导致产生多对一的关联. 指定多的一端unique="true",这样就限制了多的一端的多重性为一.

<classname="Person" table=”t_person”>

    <id name="id" >

        <generator class="native"/>

</id>

<propertyname=”name”/>

    <many-to-one name="idCard" column="addressId" unique="true"

        not-null="true"/>

</class>

这种状态注意TransientObjectException异常.在保存时就先保存外键idCard,再保存Person类.

一对一单向外键关联Annotation配置

@OneToOne

@JoinColumn(name="w_id")(不加也可以,会自动生成一个默认的名字)

一对一(双向)主键关联映射(了解)

hibernate一对一双向指的是2个对象都可以互相的调用,双向的配置可以方便我们调用对象。比如知道A对象就可以使用B对象,可以调用B对象就可以使用A对象

PersonßàIdCard.在另一端也加上一个一对一的单向关联映射.

模型对象

Person(id,name,idCard)

IdCard(id,cardNo,person)中,双方都持有对方的属性引用.

一对一(双向)主键关联映射XML配置方式

在IdCard配置中建立映射,<one-to-onename="person"/>指示Hibernate如何加载,默认情况下根据主键加载.也就是在基于单向一对一的映射中, 在另一端也加上一个单向一对一的主键关联映射.

Person一端配置

<classname="Person" table=”t_person”>

    <id name="id" >

        <generatorclass="foreign">

            <paramname="property">idCard</param>

        </generator>

    </id>

    <one-to-one name="idCard"constrained="true"/>

</class>

在另一端IdCard配置

<classname="IdCard " table=”t_idCard”>

    <id name="id" >

        <generator class="native"/>

</id>

<propertyname=”cardNo”/>

    <one-to-one name="person"property-ref=”idCard/>

</class>

一对一(双向)主键关联映射Annotation(BUG)

在两端各自的引用属性上加上

@OneToOne

@PrimaryKeyJoinColumn

 

一对一(双向)唯一外键关联映射

Personß----àIdCard.在另一端也加上一个一对一的单向关联映射.

在模型对象

Person(id,name,idCard)

IdCard(id,cardNo,person),

双方都持有对方的属性引用.

需要在另一端加上<one-to-one>,指示hibernate如何加载,默认情况下根据主键加载person;因为外键关联映射中,两个实体的关系是由person的外键idCard来维护的,所以不能指定person的主键来加载person,而应根据person的外键idCard来加载person对象.

一对一双向外键关联映射XML配置方式

Person一端:用多对一配置外键唯一形成一对一的配置方式.

<classname="Person" table=”t_person”>

    <id name="id" >

        <generator class="native"/>

</id>

<propertyname=”name”/>

    <many-to-one name="idCard" column="addressId" unique="true"/>

</class>

IdCard一端:一对一,引用另一端外键

<classname="IdCard " table=”t_idCard”>

    <id name="id" >

        <generator class="native"/>

</id>

<propertyname=”cardNo”/>

<one-to-one name="person" property-ref="idCard"/>

</class>

要想加载idCard,如果不加property-ref,默认根据person主键id来加载,property- ref="idCard"就指示hibernateperson里面的idCard属性来加载.

一对一双向外键关联映射Annotation配置方式

双方互持对方的属性的引用

关系模型

Husband(id,name,wife)

Wife(id,name,husband)

在Husband一端的wife属性上注解

@OneToOne

//@JoinColumn

在Wife一端的husband加上注解,mappedBy

@OneToOne(mappedBy="wife")引用属性

加上mappedBy属性后就可以在wife这一端告知这个关联是在wife属性上设置的.就不用管理wife这一端生成的husband的设置.生成的wife表格不会有husband字段.

规律:有双向关联时mappedBy通常必设.

 

联合主键一对一单向外键关联映射

对象模型

Wife(id,name,age)WifePk(id,name)(需要实现Serializable接口)

Husband(id,name,wife)

1在Wife中建立联合主键生成策略

  @IdClass(WifePk.Class)

 @Id

2在Husband中添加个外键即可

  @OneToOne

3自定义Husband中外键的名字

    @OneToOne

@JoinColumns(

        {

           @JoinColumn(name="wifeId", referencedColumnName="id"),

           @JoinColumn(name="wifeName", referencedColumnName="name")

        }

    )

XML配置方式:略

 

组件映射

对象关系:一个对象是另一个对象的一部分

数据库表:是一张表

Annotation:@Embeddable,@Embedded

XML:<component>

对象模型

Husband(id,name,wife)

Wife(wifeName,wifeAge)

Annotation:

在Husband的wife属性上建立注解

@Embedded 表明该对象是从别的位置嵌入过来的,是不需要单独映射的表.

这种方式生成的表为husband(id,name,wifename,wifeage),不会生成wife表.

XML:

<classname="Husband" >

       <id name="id">

           <generatorclass="native"/>    

</id>

       <propertyname="name"></property>

       <component name="wife">

           <propertyname="wifeName"/>       

<property name="wifeAge"/>

       </component>

 </class>

 

 

 

数据库设计的三大范式

第一范式:每列保证最小原子性

第二范式:是建立在第二范式的基础之上

一个表只描述一件事情

第三范式:是建立在第二范式的基础之上

        所有列与主键的关系是直接的关系

 

多对一(many to one)单向关联映射

多对一的数据库设计原则:在多的那下端加外键

//注意在创建实体类属性时应尽量避免与SQL语句中的关键字重名.

多对一单向关联映射

实体模型(User多对一Group)

User(id,name,group)

Group(id,groupname)

 

Annotation配置

@Entity

@Table(name="t_group")//注意表名与SQL中关键字重名

只需要在多的一端User属性group进行注解配置

@ManyToOne

@JoinColumn(name=”groupId”)

XML配置

<many-to-onename="group"column="groupId"/>

标签会在”多”的一端添加外键,相当于在数据库中添加外键

生成的表为user(id,name,groupid),t_group(id,groupname)

属性cascade

<many-to-onename="group"column="groupid"cascade="all"/>

取值all,none,save-update,delete,对象间的级联操作,只对增删改起作用.

在存储时User时,设置了cascade="all"会自动存储相应的t_group.而不用管user关联的对象(通常情况下会优先存储关联的对象,然后再存储user).

 

一对多(one to many)单向关联映射

模型(group一对多user)

Group(id,name,users)一

User(id,name)多

设计时在一的这一端存在着多的集合,生成的数据库表通常是在多的一端生成外键.

Set<User>users=new HashSet<User>()

一对多单向外键关联映射

在一的这一端Group端users属性上进行注解配置

@OneToMany

@JoinColumn(name="groupId")(不可少的)

如果不指定生成的外键列@JoinColumn(name="groupId"),默认会生成多对多的关系,产生一张中间表.

XML配置中配置一的那一端Group

<class name="com.hibernate.Group"table="t_group">

       <idname="id">

           <generatorclass="native"/>

       </id>

       <propertyname="name"/>

       <set name="users">

           <key column="groupId"/>指定生成外键字段的名字

           <one-to-many class="com.hibernate.User"/>

       </set>

</class>

 

 

规律:

l  单向时, 一方存在另一方的引用,数据库表中体现为外键.配置时一般只用配置一端.

l  双向时,双方互持对方的引用,要进行双向两端的配置.基于annotation的双向配置中,在一端配置好后,在另一端必须用指定mappedby属性.

l  关于一对多/多对一数据库设计时,总是在多的一端加外键.通常在多的一端指定生成的外键名称.

 

一对多/多对一双向关联

一对多与多对一的双向关联是同一种情况.

关系模型(group一对多user)

Group(id,name,users)一

User(id,name,group)多

Set<User>users=new HashSet<User>()

配置规则:一般以多的一端为主,先配置多的一端

在多的一端User端配置group

@ManyToOne

在一的一端Group端配置时,在users只需要加个mappedBy="group"

@OneToMany(mappedBy="group")

XML配置

Group中

<setname="users">

    <keycolumn="groupId"/>        

<one-to-manyclass="com.hibernate.User"/>

</set>

在User中

<many-to-onename="group"column="groupId"/>

务必确保在多的一端生成的生成的外键和一的一方生成的外键的名字相同,都为groupId.

如果名字不同则会在多的一端生成多余的外键.

create tablet_group (

        id integer not null auto_increment,

        name varchar(255),

        primary key (id)

    )

create tablet_user (

        id integer not null auto_increment,

        name varchar(255),

        groupId integer,

        primary key (id)

    )

alter tablet_user

        add index FKCB63CCB6C3D18669 (groupId),

        add constraint FKCB63CCB6C3D18669

        foreign key(groupId) references t_group (id)

 

多对多单向关联

关系举例:老师à学生,老师需要知道自己教了哪些学生,但学生不知道自己被哪些老师来教.

数据库:中间表

Annotation:@ManyToMany

XML:<many-to-many>

关系模型(Teache多对多Student),从Teacher这一端能关联到students.

Teacher(id,name,students)多

Student(id,name)多

Set<Student>students=new HashSet<Student>()

在Teacher那一端配置

@ManyToMany

如果手动指定生成的中间表的表名和字段名

@JoinTable(

name="t_s",  //表名

    joinColumns={@JoinColumn(name="teacher_id")},//指向teacher表

    inverseJoinColumns={@JoinColumn(name="student_id")}//指向另一张表

       )

生成的表为

create tableStudent (

        id integer not null auto_increment,

        name varchar(255),

        primary key (id)

)

create tableTeacher (

        id integer not null auto_increment,

        name varchar(255),

        primary key (id)

)

create table t_s(//生成的中间表

        teacher_id integer not null,

        student_id integer not null,

        primary key(teacher_id, student_id)

    )

t_s表的两个属性分别references其它表的主键.

XML中

<class name="com.xxx.Teacher">

       <idname="id">

           <generatorclass="native"/>

       </id>

       <propertyname="name"/>

       <set name="students" table="t_s">table定义中间表的表名

           <key column="teacher_id"></key>

           <many-to-many class="com.xxx.Student" column="student_id"/>

       </set>

</class>

 

多对多双向关联

关系举例:老师ßà学生,老师需要知道自己教了哪些学生,学生也知道自己有哪些老师.

数据库:中间表

Annotation:@ManyToMany

XML:<many-to-many>

多对多单向配置只需要在一端进行配置就可以了.

关系模型(Teache多对多Student)

Teacher(id,name,students)多

Student(id,name,teachers)多

Set<Student>students=new HashSet<Student>()

Set<Teacher> teachers = newHashSet<Teacher>();

Annotation配置

在Teacher这一端的students上配置

@ManyToMany

    @JoinTable(name="t_s",

       joinColumns={@JoinColumn(name="teacher_id")},

       inverseJoinColumns={@JoinColumn(name="student_id")}

       )

在Student一端的teachers只需要配置

@ManyToMany(mappedBy="students")

XML配置方式:两端配置一样,注意表名和生成的中间表的字段属性名要一致

Teacher那一端配置

<setname="students"table="t_s">

    <key column="teacher_id"/>

    <many-to-many class="com.xxx.Student"column="student_id"/>

</set>

在Student那一端配置

<setname="teachers"table="t_s">

    <key column="student_id"></key>

    <many-to-many class="com.xxx.Teacher"column="teacher_id"/>

</set>

生成的数据库表和上面是一样的.

 

 

 

     

树状结构的设计(重要)

设计思想:数据库模型,面向对象模式,关系映射,CRUD

数据库模型:表(id,name,pid)

实体模型:父结点ß一对多à子结点,一对多/多对一双向关联映射,一个子结点只有一个父结点,一个父结点有多个子结点.

ClassOrg

private int id;

    privateString name;

    privateSet<Org> children = newHashSet<Org>();

    private Org parent;

关系映射:在同一个类中使用@ManyToOne和@OneToMany

在父结点parent

@ManyToOne  

@JoinColumn(name="parent_id")

public OrggetParent() {

       return parent;

    }

在子结点children

@OneToMany(cascade=CascadeType.ALL, mappedBy="parent")

publicSet<Org> getChildren() {

       return children;

    }

 

 

基本关联关系对象的CRUD

1想删除或者更新,先做load,除非精确知道ID.

2Cascade属性(是一个数组)管理CUD,只是为了编程的方便,不要把它的功能看得太大.

cascade={CascadeType.ALL}

CascadeType取值

ALL    Cascade all operations所有情况

MERGE Cascade merge operation合并(merge=save+update)

PERSIST  Cascade persist operation存储 persist()(只有调用persist()方法才有效果,save()无效)

REFRESH Cascade refresh operation刷新

REMOVE   Cascade remove operation删除

规律,双向关系要是程序中设定双向关联.还要设置mappedBy

 演示案例:1.保存单方关联对象,另外一方自动保存

         2.读取对象时,读取many时会自动读取出one的一方,读取one时不会自动读取many (Cascade不会影响读取)

         3.删除对象时会自动删除关联对象(不设置会将多的一方对应一得一方id为null)

        

3Fetch属性,读取,管理R(Retrieve查询)

fetch = FetchType.LAZY|EAGER

HibernateAnnotation的默认的FetchType在ManyToOne是EAGER的,在OneToMany上默认的是LAZY.

如果指定OneToOnefetch=FetchType.LAZY,会延迟对关联对象的加载,不管是load还是get.

在XML中,在外键属性上设置inverse=”true|false”进行设置.

 

 

 

 

Hibernate查询(HQL)

Hql 语句对表还是对对象操作

QL(QueryLanguage)

NativeSQL(数据库的本地语言)>HQL>QBC(Query by Criteria)

 

参数绑定形式

a)  按参数位置绑定

b)  按参数名称绑定

链式编程

Query q =session.createQuery("from Category c where c.id > :min andc.id < :max")

    .setInteger("min", 2)

    .setInteger("max", 8);

 

Query q = session.createQuery("fromCategory c where c.id > ? and c.id < ?");

q.setParameter(0,2)

.setParameter(1,8);

 

分页显示

Query q =session.createQuery("from Category c order by c.name desc");

q.setFirstResult(2);//设置起始记录位置.

q.setMaxResults(4);//设置每页显示的最大记录数

 

q.uniqueResult()//返回唯一的一条记录.

 

Isnull

Isempty 测试集合是否为空

 

Criteria

  Criteriacriteria=session.createCriteria(Group.class);

       List<Group> groups= criteria.list();

 条件查询

a)  比较运算 

查询部门名称为CLERK  Restrictions.eq()

Criteria criteria=session.createCriteria(Emp.class);

       Criterioncriterion=Restrictions.eq("job","CLERK");

       criteria=criteria.add(criterion);

       List<Emp>emps=  criteria.list();

查询薪水大于2000  Restrictions.gt()

Criteria criteria=session.createCriteria(Emp.class);

       Criterioncriterion=Restrictions.gt("sal", 2000);

       criteria=criteria.add(criterion);

       List<Emp>emps=  criteria.list();

查询没有奖金Restrictions.isNull()

     Criteriacriteria=session.createCriteria(Emp.class);

       Criterioncriterion=Restrictions.isNull("comm");

       criteria=criteria.add(criterion);

b)  范围运算 Restrictions.in( )

c)  字符串模式匹配 Restrictions.ilike( )

d)  逻辑运算 Restrictions.and( )

e)  集合运算 Restrictions.isEmpty( )

性能优化  get 3.2s   load 3s

1注意session.clear()的运用,尤其是不断分页循环的时候

  A 在一个大集合中进行遍历,取出其中含有敏感字的对象,把敏感字换成别的

  B 另一种形式的内存泄露.

面试题:java有内存泄露吗?语法级别上没有,jvm自动回收,但是在写程序的过程中,用到了一些资源,一定记得回收。比如:打开了连接池,打开了连接 用完记得关闭,不然在内存中老开着,再比如,打开了文件记得关闭( java调用了 C  C调用了window API,C语言是手动回收内存的 其实是java引起了内存泄露)

21+N问题 面试题

 LAZY ,BatchSize,join fetch

3list和iterator的区别

list取所有

 iterator先取ID,等到要用的时候再根据ID取出对象.

 session中list第二次发出,仍会到数据库中查询数据.iterator第二次首先查找session级缓存.

 

4一级缓存和二级缓存和查询缓存

 一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个session(一定要同一个session)又做了同一个操作,那么hibernate直接从一级缓存中拿,而不会再去连数据库,取数据。
二级缓存就是SessionFactory级别的缓存,顾名思义,就是查询的时候会把查询结果缓存到二级缓存中,如果同一个sessionFactory创建的某个session执行了相同的操作,hibernate就会从二级缓存中拿结果,而不会再去连接数据库。

A缓存

B一级缓存,session级别的缓存

C二级缓存,SessionFactory级别的缓存,可以跨越session级别存在.

适合放二级缓存:

经常被访问,改动不大不会经常访问,数量有限.如:用户权限,组织机构

D打开二级缓存

 Hibernate.cfg.xml

<propertyname="cache.use_second_level_cache">true</property>

使用ehcache

JAR文件:\lib\optionalehcache-1.2.3.jar

<propertyname="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

使用配置文件ehcache.xml,将其复制到src目录下.

Annotation注解:@Cache

importorg.hibernate.annotations.Cache;

importorg.hibernate.annotations.CacheConcurrencyStrategy;

@Cache(usage =CacheConcurrencyStrategy.READ_WRITE)

@Cache(

   CacheConcurrencyStrategy usage();                 (1)

   String region() default "";                       (2)

   String include() default "all";                   (3)

)

(1)   usage: 给定缓存的并发策略(NONE,READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL)

(2)   region (可选的):缓存范围(默认为类的全限定类名或是集合的全限定角色名)

(3)   include (可选的):值为all时包括了所有的属性(proterty),为non-lazy时仅含非延迟属性(默认值为all)

Eload默认使用二级缓存,iterator默认使用二级缓存

Flist默认向二级缓存添加数据,但是查询的时候不使用.

G 如果Query需要使用二级缓存,则打开查询缓存

<property name="cache.use_query_cache">true</property>

需要调用Query setCachable(true)方法指明使用二级缓存.

 

 

 

 

0 0