Hibernate笔记2

 

 

 

 

 

hibernate是什么?

1.    hibernate 是一个框架(framework)

2.    hibernate 是一个orm框架 []

l  orm (object relation mapping) 对象关系映射框架

 

o object -> 业务层(只对对象操作)

r relation-> 关系数据库

m mapping 对象关系映射文件

3.    hibernate 处于我们项目的持久层位置(正因为如此,所以有人又把hibernate称为 持久层框架)

4.    hibernate 实际上就是对jdbc进行了轻量级的封装.

5.    hibernate 的基础还是我们java 反射机制

 

l  除了hiberante 这个orm框架,还有一些:

apache ojb / toplink / ibatis / ejb cmp

Apache OJB ()

  Cayenne ()

  Jaxor ()

  Hibernate ()

  iBatis ()

  jRelationalFramework()

  mirage ()

  SMYLE ()

  TopLink ()

 

 

把对象持久化: 把对象的信息保存到数据库或者是文件.

 

总结: hibernate 是对jdbc进行轻量级封装的  orm 框架,充当项目的持久层.

 

u  为什么需要hibernate?

 

 

u  快如入门案例:

 

hiberante 可以用在 j2se 项目,也可以用在 j2ee(web项目中)

struts是web框架,所以用在 web项目

 

我们使用手动配置hibernate方式开发一个hibernate 项目,完成crud操作 。

 

开发流程

1.    创建一个项目

2.    画出一个简单项目框架示意图

3.    引入hibernate 开发包 (从网上下载google  hibernate  http://www.hibernate.org),完后我们

4.    开发hibernate 有三种方法(开发顺序)

我们使用第二种开发项目

创建employe 表.

create table employee(

id number primary key,

name varchar2(64) not null,

email varchar2(64) not null,

hiredate date not null)

 

创建一个序列,将来用于主键的自增长 :

--创建一个序列

create sequence emp_seq

start with 1

increment by 1

minvalue 1

nomaxvalue

nocycle

nocache

 

5.    开发domain对象和对象关系映射文件

 

对象关系映射文件: 作用是用于指定 domain对象和表的映射关系. ,该文件的取名有规范:

domain对象.hbm.xml,一般我们放在 和domain对象同一个文件夹下(包下)

 

我们的Employee.hbml.xml配置文件 :

<!DOCTYPE hibernate-mapping PUBLIC

       "-//Hibernate/HibernateMapping DTD 3.0//EN"

       "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

 

<hibernate-mappingpackage="com.hsp.domain">

       <classname="Employee" table="employee">

       <!--id元素用于指定主键属性 -->

       <idname="id" column="id"type="java.lang.Integer">

       <!--该元素用于指定主键值生成策略hilonative increment sequence uuid -->

       <generatorclass="sequence">

       <paramname="sequence">emp_seq</param>

       </generator>

       </id>

       <!--对其它属性还有配置-->

       <propertyname="name" type="java.lang.String">

       <columnname="name" not-null="false"  />

       </property>

       <propertyname="email" type="java.lang.String" >

       <columnname="email" not-null="false"/>

       </property>

       <propertyname="hiredate" type="java.util.Date">

       <columnname="hiredate" not-null="false" />

       </property>

       </class>

      

</hibernate-mapping>

 

6.    手动配置我们的hibernate.cfg.xml文件,该文件用于配置 连接的数据库的类型,driver,

,用户名,密码 ,url ....同时管理 对象关系映射文件 ,该文件的名称,我们一般不修改.

 

hibernate.cfg.xml配置文件

<?xml version="1.0"encoding="utf-8"?>

<!DOCTYPE hibernate-configuration PUBLIC

       "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

       "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

       <!--hibernate 设计者,给我们提供了一写常用的配置 -->

       <!--配置使用的driver-->

       <propertyname="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>

       <propertyname="connection.username">scott</property>

       <propertyname="connection.password">tiger</property>

       <propertyname="connection.url">jdbc:oracle:thin:@127.0.0.1:1521:orclhsp</property>

       <!--配置dialect方言,明确告诉hibernate连接是哪种数据库 -->

       <propertyname="dialect">org.hibernate.dialect.OracleDialect</property>

       <!--显示出对于sql-->

       <propertyname="show_sql">true</property>

       <!--指定管理的对象映射文件-->

       <mappingresource="com/hsp/domain/Employee.hbm.xml"/>

</session-factory>

</hibernate-configuration>

 

7.    测试文件TestMain.java

package com.hsp.view;

import com.hsp.util.*;

import java.util.Date;

 

 

 

import org.hibernate.Session;

import org.hibernate.SessionFactory;

import org.hibernate.Transaction;

import org.hibernate.cfg.*;

 

import com.hsp.domain.Employee;

public class TestMain {

 

       /**

        * @param args

        */

       publicstatic void main(String[] args) {

              //查询[load]->hql语句(hibernate query language)

       }

       publicstatic void delEmp() {

              //删除

              //获取一个session

              Sessionsession=MySessionFactory.getSessionFactory().openSession();

              Transactionts=session.beginTransaction();

              //删除1.先获取该雇员,然后删除

              Employee emp=(Employee)session.load(Employee.class, 3);

              session.delete(emp);

              ts.commit();

              session.close();

       }

 

 

 

       publicstatic void updateEmp() {

              // TODO Auto-generated method stub

              //修改用户

              //获取一个会话

              Sessionsession=MySessionFactory.getSessionFactory().openSession();

             

              Transactionts=session.beginTransaction();

              //修改用户1. 获取要修改的用户,2.修改

              //load是通过主键属性,获取该对象实例.<--->表的记录对应

              Employee emp=(Employee)session.load(Employee.class, 3);

              emp.setName("韩顺平8");//update...

              emp.setEmail("abc@sohu.com");

              ts.commit();

              session.close();

       }

 

      

      

       publicstatic void addEmployee() {

              //我们使用hibernate完成crud操作[这里我们只见对象,不见表]

              //现在我们不是用service ,直接测试.

              //1。创建Configuration,该对象用于读取hibernate.cfg.xml,并完成初始化

              Configuration configuration=newConfiguration().configure();

              //2.创建SessoinFactory[这是一个会话工厂,是一个重量级的对象]

              SessionFactorysessionFactory=configuration.buildSessionFactory();

              //3.创建Sessoin 相当于jdbc Connection[ servelt HttpSession ,也不是 jsp session]

              Sessionsession=sessionFactory.openSession();

              //4.对hiberate而言,要求程序员,在进行 增加,删除,修改的时候使用事务提交,

              Transaction transaction =session.beginTransaction();

              //添加一个雇员

              Employee employee=new Employee();

              employee.setName("shunping");

              employee.setEmail("shunping@sohu.com");

              employee.setHiredate(new Date());

              //insert .............

              //保存

              session.save(employee);//save employee就是持久化该对象 (把对象保存到了数据库中称为一条记录)

              //==>insert into ....[被hiberante封装]

              //提交

              transaction.commit();

              session.close();

       }

 

}

 

u  现在我们体验一下hibernate切换数据库的优势.

这次,我们使用 让hibernate自动完成 domain->映射文件->表 的工作.

1.    首先我们把  hibernate.cfg.xml文件重新配置.

<?xml version="1.0"encoding="utf-8"?>

<!DOCTYPE hibernate-configuration PUBLIC

        "-//Hibernate/Hibernate ConfigurationDTD 3.0//EN"

       "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

       <!--hibernate 设计者,给我们提供了一写常用的配置 -->

       <!--配置使用的driver-->

       <propertyname="connection.driver_class">com.mysql.jdbc.Driver</property>

       <propertyname="connection.username">root</property>

       <propertyname="connection.password">root</property>

       <propertyname="connection.url">jdbc:mysql:/localhost:3306/test</property>

       <!--配置dialect方言,明确告诉hibernate连接是哪种数据库 -->

       <propertyname="dialect">org.hibernate.dialect.MySQLDialect</property>

       <!--显示出对于sql-->

       <propertyname="show_sql">true</property>

       <!--让hibernate给我们自动创建表 create :如果没有该表则创建. -->

       <propertyname="hbm2ddl.auto">create</property>

       <!--指定管理的对象映射文件-->

       <mappingresource="com/hsp/domain/Employee.hbm.xml"/>

</session-factory>

</hibernate-configuration>

2.    对对象映射文件,做了相应的修改.

       <!--对主键生成策略我们做了修改-->

       <idname="id" column="id"type="java.lang.Integer">

       <generatorclass="increment"></generator>

       </id>

 

笔试题: 请列举出hibernate常见的接口和类

 

?请解释什么事pojo类,它有什么要求:

1.    pojo类是和一张表对应

2.    一般我们放在 com.xxx.domain下

3.    pojo 需要一个主键属性(用于标示一个pojo对象)

4.    除了主键属性外,它应当还有其属性,属性的访问权限是private

5.    提供 set /get 方法

6.    它应当有一个无参的构造方法(hibernate 反射)

7.    pojo类其实就是javabean/ 有些老程序员 叫他  date对象

 

 

上机练习:

写一个简单的雇员管理系统.

emp( id , name , tel , birthday ),

可以,进入主界面

 

 

请选择数据库类型

1.    表示 连接 oracle

2.    表示 连接 mysql

3.    表示 连接 sql server

 

 

请选择操作

1.    显示所有雇员

2.    根据id查询指定雇员

3.    修改雇员信息(请先输入id)

4.    根据id 删除雇员.

 

 

* 用户可以多次选择操作.

 

 

 

 

u  hibernate的核心类和接口

①     Configuration 类

它的用处是:

1.    读取hibernate.cfg.xml

2.    管理对象关系映射文件 <mapping resource=””>

3.    加载hibernate 的驱动,url ,用户..

4.    管理hibernate配置信息

 

②     hibernate.cfg.xml

③     对象关系映射文件

④     SessionFactory (会话工厂)

1.    可以缓存sql语句和数据(称为session级缓存)!!

2.    是一个重量级的类,因此我们需要保证一个数据库,有一个SessionFactroy

这里我们讨论一个通过SessionFactory获取 Session的两个方法 openSession() 一个 getCurrentSession();

1.    openSession() 是获取一个新的session

2.    getCurrentSession () 获取和当前线程绑定的session,换言之,在同一个线程中,我们获取的session是同一session,这样可以利于事务控制

如果希望使用getCurrentSession 需要配置 hibernate.cfg.xml中配置.

3.    如何选择

原则:

       ①如果需要在同一线程中,保证使用同一个Session则,使用getCurrentSession()

       ②如果在一个线程中,需要使用不同的Session,则使用opentSession()

4.    通过 getCurrentSession() 获取的session在事务提交后,会自动关闭,通过openSession()获取的session则必须手动关闭

5.    如果是通过getCurrentSession() 获取 sesssion ,进行查询需要事务提交.

 

全局事务和本地事务

jndi

 

l  如何确定你的session有没有及时关闭

windows  cmd netstat –an      [oracle 1521mysql 3306 sql server 1433]

linux/unix  netstat –anp top 

 

⑤     session接口

它的主要功能和作用是:

1.      Session一个实例代表与数据库的一次操作(当然一次操作可以是crud组合)

2.      Session实例通过SessionFactory获取,用完需要关闭。

3.      Session是线程不同步的(不安全),因此要保证在同一线程中使用,可以用getCurrentSessiong()。

4.      Session可以看做是持久化管理器,它是与持久化操作相关的接口

 

get vs load

1.     如果查询不到数据,get 会返回 null,但是不会报错, load 如果查询不到数据,则报错ObjectNotFoundException

2.    使用get 去查询数据,(先到一级/二级)会立即向db发出查询请求(select ...), 如果你使用的是 load查询数据,(先到一级、二级))即使查询到对象,返回的是一个代理对象,如果后面没有使用查询结果,它不会真的向数据库发select ,当程序员使用查询结果的时候才真的发出select ,这个现象我们称为懒加载(lazy)

3.    通过修改配置文件,我们可以取消懒加载

<class  name="Employee"lazy="false" table="employee">

4.    如何选择使用哪个: 如果你确定DB中有这个对象就用load(),不确定就用get()(这样效率高)

 

u  我们对获取session的工具类,升级,让它可以直接返回 全新的session和线程相关的session

代码:

packagecom.hsp.util;

importorg.hibernate.Session;

importorg.hibernate.SessionFactory;

importorg.hibernate.cfg.Configuration;

final publicclass HibernateUtil { //SqlHelper

     private static SessionFactorysessionFactory=null;

     //使用线程局部模式

     private static ThreadLocal<Session>threadLocal=new ThreadLocal<Session>();

     private HibernateUtil(){};

     static {

            sessionFactory=newConfiguration().configure("com/hsp/config/hsp.cfg.xml").buildSessionFactory();

     }

    

     //获取全新的全新的sesession

     public static Session openSession(){

            returnsessionFactory.openSession();

     }

     //获取和线程关联的session

     public static Session getCurrentSession(){

           

            Sessionsession=threadLocal.get();

            //判断是否得到

            if(session==null){

                   session=sessionFactory.openSession();

                   //把session对象设置到 threadLocal,相当于该session已经和线程绑定

                   threadLocal.set(session);

            }

            returnsession;

           

           

     }

    

    

}

 

u  query接口

通过query接口我们可以完成更加复杂的查询任务.

举例: 通过用户来查询数据.

快如入门:

Sessionsession=HibernateUtil.getCurrentSession();

              Transactionts=null;

             

              try{

                    

                     ts=session.beginTransaction();

                    

                     //获取query引用[这里 Employee不是表.而是domain类名]

                     //[where 后面的条件可以是类的属性名,也可以是表的字段,安照hibernate规定,我们还是应该使用类的属性名.]

                     Queryquery=session.createQuery("from Employee where namehsp='shunping'");

                     //通过list方法获取结果,这个list会自动的将封装成对应的domain对象

                     //所以我们jdbc进行二次封装的工作没有.

                     List<Employee> list=query.list();

                     for(Employee e: list){

                            System.out.println(e.getAaaid()+""+e.getHiredate());

                     }

                    

                     ts.commit();

                    

              }catch (Exception e) {

                    

                     if(ts!=null){

                            ts.rollback();

                     }

                     throw newRuntimeException(e.getMessage());

              }finally{

                     //关闭session

                     if(session!=null&&session.isOpen()){

                            session.close();

                     }

                    

              }

u  criteria 接口的简单使用

快如入门:

Sessionsession=HibernateUtil.getCurrentSession();

              Transactionts=null;

             

              try{

                    

                     ts=session.beginTransaction();

                    

                     Criteriacri=session.createCriteria(Employee.class).

                     setMaxResults(2).addOrder(Order.desc("id"));

                     List<Employee> list=cri.list();

                     for(Employee e: list){

                            System.out.println(e.getAaaid());

                     }

                    

                     ts.commit();

                    

              }catch (Exception e) {

                    

                     if(ts!=null){

                            ts.rollback();

                     }

                     throw newRuntimeException(e.getMessage());

              }finally{

                     //关闭session

                     if(session!=null&&session.isOpen()){

                            session.close();

                     }

                    

              }

 

u  如何使用eclipse进行hibernate 快速开发

我们以前面讲的对employee表进行crud为例,演示具体用法

 

手动配置:

db(table )->手写domain对象->对象关系映射文件

现在我们希望用工具完成 Domain对象和 关系映射文件的工作.

 

1.    创建web项目

2.    通过myeclipse 提供 数据库浏览器连接到我们的oracle数据库(多人开发)

* 这里请大家小心,如果我们测试

 

你们把自己的数据库通过 db 浏览器连接上

 

引入hibernate开发包.,同时自动创建hibernate.cfg.xml文件,如果希望把hibernate开发包升级,我们可以重新引入包.

 

 

下面我们使用myeclipse提供的逆向工程,自动的创建domain类和对象关系映射文件.

 

 

java对象(属性) <---------1. java类型 2. hibernate types------------->表字段类型

 

拉通练习一把.

 

 

 

 

u  为什么要学习hql(hibernate querylanguage)->这个是官方推荐,功能强大

? 删除

session.delete(对象) -> 批量删除

? 添加

session.save  session.persist

? 修改->批量修改

sessin.update(对象)

 

查询 对象 obj

obj.setXXX();

? 查询

load get

查询所有 性别是男的雇员?

 

u  hql的详解

为了讲解清楚,我模拟一个学生选课系统 ,创建三张表

从创建的三张表,我们看出:

hibernate 设计者推荐我们在设计表的时候,应当每张表有一个主键,而且该主键最好不含业务逻辑, 

 

product 表

id  productNo   name      price

1     bi001       冰箱       1000

2     nj111       电脑       2000 

 

我们现在使用hibernate工具,自动生成 domain 对象 和映射文件,如果我们的表有主外键的关系,则应当先映射主表,再映射从表

 

* uniqueResult方法

如果我们检索一个对象,明确知道最多只有一个对象,则建议使用该方法:

具体用法如下:

Student s=(Student)session.createQuery("from Student wheresid='20050003'").uniqueResult();

                     System.out.println(s.getSname());

*distinct的用法

过滤重复的记录

//比如,显示所有学生的性别和年龄.

                     Listlist=session.createQuery("select distinct sage,ssex fromStudent").list();

                     for(inti=0;i<list.size();i++){

                            Object [] objs=(Object[]) list.get(i);

                            System.out.println(objs[0].toString()+""+objs[1].toString());

                     }

*between and..

List list=session.createQuery("selectdistinct sage,ssex,sname from Student where sage between 20 and22").list();

                     for(inti=0;i<list.size();i++){

                            Object [] objs=(Object[]) list.get(i);

                            System.out.println(objs[0].toString()+""+objs[1].toString()+objs[2].toString());

                     }

*in /not in

//查询计算机系和外语系的学生信息

                    

                     List<Student>list=session.createQuery("from Student where sdept in ('计算机系','外语系')").list();

                     //取出1. for 增强

                     for(Students:list){

                            System.out.println(s.getSname()+""+s.getSaddress()+" "+s.getSdept());

                     }

*group by使用

//显示各个系的学生的平均年龄

List<Object[]>list=session.createQuery("select avg(sage),sdept from  Student group by sdept").list();

                     //取出1. for 增强

                     for(Object[]obj:list){

                            System.out.println(obj[0].toString()+""+obj[1].toString());

                     }

 

 

//having的使用

                     //1.对分组查询后的结果,进行筛选:比如请显示人数大于3的系名称

                     //a.查询各个系分别有多少学生.

                    

                     List<Object[]>list=session.createQuery("select count(*) as c1,sdept from  Student group by sdept havingcount(*)>3").list();

                     //取出1. for 增强

                     for(Object[]obj:list){

                            System.out.println(obj[0].toString()+""+obj[1].toString());

                     }

//2查询女生少于200人的系

                     //a.查询各个系的女生有多个个

                     List<Object[]>list=session.

                     createQuery("selectcount(*) as c1,sdept from  Student wheressex='F' group by sdept").list();

                     //取出1. for 增强

                     for(Object[]obj:list){

                            System.out.println(obj[0].toString()+""+obj[1].toString());

                     }

//1.查询计算机系共多少人?->如果我们返回的是一列数据

                     //这时我们的取法是直接取出list->object 而不是 list->Object[]

                     List<Object[]>list=session.

                     createQuery("selectsage from  Student where sdept='计算机系'").list();

                     //取出1. for 增强

                     for(Objectobj:list){

                            System.out.println(obj.toString());

                     }

3.查询选修11号课程的最高分和最低分.

                     List<Object[]> list=session.

                     createQuery("select11,max(grade),min(grade) from Studcourse where course.cid=11").list();

                     //取出1. for 增强

                     for(Object[] obj:list){

                            System.out.println(obj[0].toString()+"max="+obj[1].toString()+" min="+obj[2].toString());

                     }

//计算各个科目不及格的学生数量.(学生练习!)

                    

                     List<Object[]>list=session.

                     createQuery("selectcount(*),student.sdept from Studcourse where grade<60 group bystudent.sdept").list();

                     //取出1. for 增强

                     for(Object[]obj:list){

                            System.out.println(obj[0].toString()+""+obj[1].toString());

                     }

参数绑定案例 (jdbc->PreparedStatement setXXX)

使用参数绑定的好处有3:

1.    可读性提高, 2 效果高 3,防止 sql注入漏洞

? 面试题: 如果不使用参数绑定,怎样防止登录时, sql注入?

name password 

思路: 1. 通过用户名,查询出该用户名在数据库中对应的密码,然后再与用户输入的秘密比较,如果相等,则用户和法,否则,非法.

 

参数绑定有两种形式

Query q=session.createQuery(from Studentwhere sdept=:dept and sage>:age)

 

如果我们的参数是 :冒号形式给出的,则我们的参数绑定应当这样:

List<Student>list=session.createQuery("from Student where sdept=:a1 andsage>:sage")

                     .setString("a1","计算机系").setString("sage","2").list();

还有一种形式:

Query q=session.createQuery(from Studentwhere sdept=? and sage>?)

如果我们的参数是以 ? 形式给出的则,参数绑定应当:

List<Student>list=session.createQuery("from Student where sdept=? and sage>?")

                     .setString(0,"计算机系").setString(1,"2").list();

 

参数的绑定,可以分开写:形式如下:

Query query=session.createQuery("fromStudent where sdept=? and sage>?");

                    

                     query.setString(0,"计算机系");

                     query.setString(1,"2");

                     List<Student> list=query.list();

                     for(inti=0;i<list.size();i++){

                            Student s= list.get(i);

                            System.out.println(s.getSname()+""+s.getSage());

                     }

把HibernateUtil升级了

u  在映射文件中得到hql语句

hibernate提供了一种更加灵活的查询方法:

把hql语句配置到 对象关系映射文件,

<query name="myquerytest">

       <![CDATA[selectsname,ssex from Student where sage>22]]>

       </query>

在程序中,我们这样获取并执行:

Listlist=session.getNamedQuery("myquerytest").list();

              System.out.println(list.size());

              Iterator it=list.iterator();

              while(it.hasNext()){

                     Objectobj[]=(Object[])it.next();

                     System.out.println("n="+obj[0]);

       }

 

hibernate 对象的三种关系:

 

1.    one – to – one : 身份证<--->人

2.    one – to – many  部门 <---> 员工

3.    many-to-one   员工<--->部门

4.    many-to-many  学生<--->老师

 

criterial使用:

//查询年龄大于10岁的学生 criteria

             

              Sessions=HibernateUtil.getCurrentSession();

              Transaction tx=s.beginTransaction();

              Criteriacri=s.createCriteria(Student.class);

              //添加检索条件

              cri.add(Restrictions.gt("sage",new Long(10)));

              List<Student> list=cri.list();

              for(Student s1: list){

                     System.out.println(s1.getSname());

              }

             

              tx.commit();      

 

hibernate开发的三种方式中的:

编写domainobject + 映射文件------> 创建出对应的数据库,

这里我们说明如果要自动的创建出对应的数据库,需要做配置(hibernate.cfg.xml).

<propertyname="hbm2ddl.auto">create</property>

这里有四个配置值:create , update , create-drop, validate

create : 当我们的应用程序加载hibernate.cfg.xml [ new Configuration().config(); ]就会根据映射文件,创建出数据库, 每次都会重新创建, 原来表中的数据就没有!!!

update: 如果数据库中没有该表,则创建,如果有表,则看有没有变化,如果有变化,则更新.

create-drop: 在显示关闭 sessionFactory时,将drop掉数据库的schema

validate: 相当于每次插入数据之前都会验证数据库中的表结构和hbm文件的结构是否一致

 

l  在开发测试中,我们配置哪个都可以测试,但是如果项目发布后,最好自己配置一次,让对应的数据库生成,完后取消配置,

 

 

u  domain对象的细节:

1.    需要一个无参的构造函数(用于hibernate反射该对象)

2.    应当有一个无业务逻辑的主键属性.

3.    给每个属性提供 get set方法.

4.    在domian对象中的属性,只有配置到了对象映射文件后,才会被hiberante管理.

5.    属性一般是private范围

 

u  对对象关系映射文件的说明

对象关系文件中,有些属性是可以不配,hibernate会采用默认机制,比如

<class table=”?” > table 值不配,则以类的小写做表名

<property type=”?”> type不配置,则hibernate会根据类的属性类型,选择一个适当的类型

 

hibernate对象的三种状态,转换图:

面试图:如果判断一个对象处于怎样的状态?

主要的依据是: 1. 看该对象是否处于session, 2, 看在数据库中有没有对应的记录

瞬时态: 没有session管理,同时数据库没有对应记录

持久态: 有session管理,同时在数据库中有记录

脱管态/游离态: 没有session管理,但是在数据库中有记录.

 

u  懒加载:

简述: 当我们查询一个对象的时候,在默认情况下,返回的只是该对象的普通属性,当用户去使用对象属性时,才会向数据库发出再一次的查询.这种现象我们称为 lazy现象.

解决方法可以这样:

1.    显示初始化 Hibernate.initized(代理对象)

2.    修改对象关系文件 lazy  改写 lazy=false

3.    通过过滤器(web项目)openSessionInView

 

u  hibernate对象的关系映射

1.     many-to-one

2.    one-to-many

看一个需求: 通过一个部门号(1),来获取该部门的所有学生?

这里请大家参考代码:

 

3       one-to-one

一对一有有两种方式

(1)    基于主键的一对一

 

原理图 :

 

 

 

(2)   基于外键的一对一

 

原理图 :

 

3.     many-to-many

 

学生<--->课程 就是many-to-many

顾客<--->商品

qq<--->qq群

 

 

上机练习,

请自己想出 生活中

one-to-many

many-to-one

many-to-many

one-to-one

的四组对象关系,并使用hibernate实现写出

对应的domainobject和对象关系映射文件,并通过hbm2ddl 自动创建成功对应的表

 

 

 

 

 

u  为什么要学习hql(hibernate querylanguage)->这个是官方推荐,功能强大

? 删除

session.delete(对象) -> 批量删除

? 添加

session.save  session.persist

? 修改->批量修改

sessin.update(对象)

 

查询 对象 obj

obj.setXXX();

? 查询

load get

查询所有 性别是男的雇员?

 

u  hql的详解

为了讲解清楚,我模拟一个学生选课系统 ,创建三张表

从创建的三张表,我们看出:

hibernate 设计者推荐我们在设计表的时候,应当每张表有一个主键,而且该主键最好不含业务逻辑, 

 

product 表

id  productNo   name      price

1     bi001       冰箱       1000

2     nj111       电脑       2000 

 

我们现在使用hibernate工具,自动生成 domain 对象 和映射文件,如果我们的表有主外键的关系,则应当先映射主表,再映射从表

 

* uniqueResult方法

如果我们检索一个对象,明确知道最多只有一个对象,则建议使用该方法:

具体用法如下:

Student s=(Student)session.createQuery("from Student wheresid='20050003'").uniqueResult();

                     System.out.println(s.getSname());

*distinct的用法

过滤重复的记录

//比如,显示所有学生的性别和年龄.

                     Listlist=session.createQuery("select distinct sage,ssex from Student").list();

                     for(inti=0;i<list.size();i++){

                            Object [] objs=(Object[]) list.get(i);

                            System.out.println(objs[0].toString()+""+objs[1].toString());

                     }

*between and..

List list=session.createQuery("selectdistinct sage,ssex,sname from Student where sage between 20 and22").list();

                     for(inti=0;i<list.size();i++){

                            Object [] objs=(Object[]) list.get(i);

                            System.out.println(objs[0].toString()+""+objs[1].toString()+objs[2].toString());

                     }

*in /not in

//查询计算机系和外语系的学生信息

                    

                     List<Student>list=session.createQuery("from Student where sdept in ('计算机系','外语系')").list();

                     //取出1. for 增强

                     for(Students:list){

                            System.out.println(s.getSname()+""+s.getSaddress()+" "+s.getSdept());

                     }

*group by使用

//显示各个系的学生的平均年龄

List<Object[]> list=session.createQuery("selectavg(sage),sdept from  Student group bysdept").list();

                     //取出1. for 增强

                     for(Object[]obj:list){

                            System.out.println(obj[0].toString()+""+obj[1].toString());

                     }

 

 

//having的使用

                     //1.对分组查询后的结果,进行筛选:比如请显示人数大于3的系名称

                     //a.查询各个系分别有多少学生.

                    

                     List<Object[]>list=session.createQuery("select count(*) as c1,sdept from  Student group by sdept havingcount(*)>3").list();

                     //取出1. for 增强

                     for(Object[]obj:list){

                            System.out.println(obj[0].toString()+""+obj[1].toString());

                     }

//2查询女生少于200人的系

                     //a.查询各个系的女生有多个个

                     List<Object[]>list=session.

                     createQuery("selectcount(*) as c1,sdept from  Student wheressex='F' group by sdept").list();

                     //取出1. for 增强

                     for(Object[]obj:list){

                            System.out.println(obj[0].toString()+""+obj[1].toString());

                     }

//1.查询计算机系共多少人?->如果我们返回的是一列数据

                     //这时我们的取法是直接取出list->object 而不是 list->Object[]

                     List<Object[]>list=session.

                     createQuery("selectsage from  Student where sdept='计算机系'").list();

                     //取出1. for 增强

                     for(Objectobj:list){

                            System.out.println(obj.toString());

                     }

3.查询选修11号课程的最高分和最低分.

                     List<Object[]> list=session.

                     createQuery("select11,max(grade),min(grade) from Studcourse where course.cid=11").list();

                     //取出1. for 增强

                     for(Object[] obj:list){

                            System.out.println(obj[0].toString()+"max="+obj[1].toString()+" min="+obj[2].toString());

                     }

//计算各个科目不及格的学生数量.(学生练习!)

                    

                     List<Object[]>list=session.

                     createQuery("selectcount(*),student.sdept from Studcourse where grade<60 group bystudent.sdept").list();

                     //取出1. for 增强

                     for(Object[]obj:list){

                            System.out.println(obj[0].toString()+""+obj[1].toString());

                     }

参数绑定案例 (jdbc->PreparedStatement setXXX)

使用参数绑定的好处有3:

1.    可读性提高, 2 效果高 3,防止 sql注入漏洞

? 面试题: 如果不使用参数绑定,怎样防止登录时, sql注入?

name password 

思路: 1. 通过用户名,查询出该用户名在数据库中对应的密码,然后再与用户输入的秘密比较,如果相等,则用户和法,否则,非法.

 

参数绑定有两种形式

Query q=session.createQuery(from Studentwhere sdept=:dept and sage>:age)

 

如果我们的参数是 :冒号形式给出的,则我们的参数绑定应当这样:

List<Student>list=session.createQuery("from Student where sdept=:a1 andsage>:sage")

                     .setString("a1","计算机系").setString("sage","2").list();

还有一种形式:

Query q=session.createQuery(from Studentwhere sdept=? and sage>?)

如果我们的参数是以 ? 形式给出的则,参数绑定应当:

List<Student>list=session.createQuery("from Student where sdept=? and sage>?")

                     .setString(0,"计算机系").setString(1,"2").list();

 

参数的绑定,可以分开写:形式如下:

Query query=session.createQuery("fromStudent where sdept=? and sage>?");

                    

                     query.setString(0,"计算机系");

                     query.setString(1,"2");

                     List<Student> list=query.list();

                     for(inti=0;i<list.size();i++){

                            Student s= list.get(i);

                            System.out.println(s.getSname()+""+s.getSage());

                     }

把HibernateUtil升级了

u  在映射文件中得到hql语句

hibernate提供了一种更加灵活的查询方法:

把hql语句配置到 对象关系映射文件,

<query name="myquerytest">

       <![CDATA[selectsname,ssex from Student where sage>22]]>

       </query>

在程序中,我们这样获取并执行:

Listlist=session.getNamedQuery("myquerytest").list();

              System.out.println(list.size());

              Iterator it=list.iterator();

              while(it.hasNext()){

                     Objectobj[]=(Object[])it.next();

                     System.out.println("n="+obj[0]);

       }

 

hibernate 对象的三种关系:

 

1.    one – to – one : 身份证<--->人

2.    one – to – many  部门 <---> 员工

3.    many-to-one   员工<--->部门

4.    many-to-many  学生<--->老师

 

criterial使用:

//查询年龄大于10岁的学生 criteria

             

              Sessions=HibernateUtil.getCurrentSession();

              Transaction tx=s.beginTransaction();

              Criteria cri=s.createCriteria(Student.class);

              //添加检索条件

              cri.add(Restrictions.gt("sage",new Long(10)));

              List<Student> list=cri.list();

              for(Student s1: list){

                     System.out.println(s1.getSname());

              }

             

              tx.commit();      

 

hibernate开发的三种方式中的:

编写domainobject + 映射文件------> 创建出对应的数据库,

这里我们说明如果要自动的创建出对应的数据库,需要做配置(hibernate.cfg.xml).

<propertyname="hbm2ddl.auto">create</property>

这里有四个配置值:create , update , create-drop, validate

create : 当我们的应用程序加载hibernate.cfg.xml [ new Configuration().config(); ]就会根据映射文件,创建出数据库, 每次都会重新创建, 原来表中的数据就没有!!!

update: 如果数据库中没有该表,则创建,如果有表,则看有没有变化,如果有变化,则更新.

create-drop: 在显示关闭 sessionFactory时,将drop掉数据库的schema

validate: 相当于每次插入数据之前都会验证数据库中的表结构和hbm文件的结构是否一致

 

l  在开发测试中,我们配置哪个都可以测试,但是如果项目发布后,最好自己配置一次,让对应的数据库生成,完后取消配置,

 

 

u  domain对象的细节:

1.    需要一个无参的构造函数(用于hibernate反射该对象)

2.    应当有一个无业务逻辑的主键属性.

3.    给每个属性提供 get set方法.

4.    在domian对象中的属性,只有配置到了对象映射文件后,才会被hiberante管理.

5.    属性一般是private范围

 

u  对对象关系映射文件的说明

对象关系文件中,有些属性是可以不配,hibernate会采用默认机制,比如

<class table=”?” > table 值不配,则以类的小写做表名

<property type=”?”> type不配置,则hibernate会根据类的属性类型,选择一个适当的类型

 

hibernate对象的三种状态,转换图:

面试图:如果判断一个对象处于怎样的状态?

主要的依据是: 1. 看该对象是否处于session, 2, 看在数据库中有没有对应的记录

瞬时态: 没有session管理,同时数据库没有对应记录

持久态: 有session管理,同时在数据库中有记录

脱管态/游离态: 没有session管理,但是在数据库中有记录.

 

懒加载:

简述: 当我们查询一个对象的时候,在默认情况下,返回的只是该对象的普通属性,当用户去使用对象属性时,才会向数据库发出再一次的查询.这种现象我们称为 lazy现象.

解决方法可以这样:

1.    显示初始化 Hibernate.initized(代理对象)

2.    修改对象关系文件 lazy  改写 lazy=false

3.    通过过滤器(web项目)openSessionInView

 

通过openSessionInView来解决懒加载.

many-to-one 的many这方,如果你配置了

<class name="Student"lazy="false">

那么hibernate就会在 查询学生 many 方时,把它相互关联的对象也查询,这里我们可以看出,对select语句查询影响不大,

one-to-many 的one 的这方,如果你配置

<set name="stus"cascade="save-update" lazy="false">

当你去查询一个部门的时候,我们看到:

Hibernate: select department0_.id asid0_0_, department0_.name as name0_0_ from Department department0_ wheredepartment0_.id=?

Hibernate: select stus0_.dept_id asdept3_1_, stus0_.id as id1_, stus0_.id as id1_0_, stus0_.name as name1_0_,stus0_.dept_id as dept3_1_0_ from Student stus0_ where stus0_.dept_id=?

这样就会把该部门关联的学生全部返回,不管你使用否?

矛盾: 如何让我们的session范围更大.(缺点是session关闭会延时.)

 

u  hibernate对象的关系映射

1.     many-to-one

比如  Employe 和 Department

2.    one-to-many

比如  Departmetn 和 Employee

看一个需求: 通过一个部门号(1),来获取该部门的所有学生?

这里请大家参考代码:

 

3       one-to-one

一对一有有两种方式

(1)    基于主键的一对一

Person IdCard

基于主键的one-to-one指的就是 IdCard 这样主键值同时充当外键.

原理图 :

 

 

 

(2)   基于外键的一对一

 

原理图 :

 

3.     many-to-many

 

案例  学生《---》课程

 

学生<--->课程 就是many-to-many

顾客<--->商品

qq<--->qq群

 

 

上机练习,

请自己想出 生活中

one-to-many

many-to-one

many-to-many

one-to-one

的四组对象关系,并使用hibernate实现写出

对应的domainobject和对象关系映射文件,并通过hbm2ddl 自动创建成功对应的表

 

级联操作

所谓级联操作就是说,当你进行某个操作(添加/修改/删除...),就由hibernate自动给你完成.

比如: Department <---->Student 对象关系,我希望当我删除一个department ,那么就自动删除该部门的所有学生?

 

再比如: bbs项目

主帖<---->回帖 , 把主帖删除,那我们就希望把该主帖的回帖自动删除,这样我们可以使用级联(cascade)操作

\

案例:如何配置级联操作,当删除某个部门的时候,我们自动删除其学生.

首先我们在 配置文件中修改:

<!-- 配置one-to-many关系

 cascade="delete" 当删除该部门的时候(主对象,则级联删除它的学生从对象) -->

<setname="stus" cascade="delete">

<!-- 指定Student类对应的外键 -->

<keycolumn="dept_id" />

<one-to-manyclass="Student" />

</set>

 

java代码中操作:

//演示删除级联

                     //获取到某个部分

                     Department department=(Department)s.get(Department.class, 41);

                     s.delete(department);

 

u  演示save-update

配置文件:

<setname="stus" cascade="save-update">

<!-- 指定Student类对应的外键 -->

<keycolumn="dept_id" />

<one-to-manyclass="Student" />

</set>

代码:

//添加学生

Department department=new Department();

department.setName("业务部门3");

                    

Student stu1=new Student();

stu1.setName("顺平6");

//     stu1.setDept(department);

                     Studentstu2=new Student();

                     stu2.setName("小明6");

              //     stu2.setDept(department);

 

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

                     students.add(stu1);

                     students.add(stu2);

                     department.setStus(students);

                    

                     s.save(department);

说明:

在集合属性和普通属性中都能使用cascade

一般讲cascade配置在one-to-many(one的一方,比如Employee-Department),one-to-one(主对象一方)

 

 

 

 

hibernate综合运用.

以一个内部留言本来讲解.本案例本身不复杂,通过该案例我们讲解 struts+hibernate+接口编程的方式, 主要听点是程序结构的设计.

 

开始:

1.    看需求

2.    根据需求文档,我们画出程序框架图, 框架图的三个接口,暂时不写,后面我们再引入.

3.    创建一个web项目.

4.    创写web层.

4.1引入struts

42.把web层的jsp 表单 action 编写完毕,并且打通这个登录的模块

5. 引入hibernate 使用手动的方法来开发domain和对象关系文件

6. 把hibernate.cfg.xml写好

7. 写成工具类 dao HibernateUtil.java

8. 测试一下看看能否映射ok

测试成功!

9. 我们使用接口来解耦. web---接口--业务层

10. hibernate中,取消lazy 不要在 one-to-many 的 one的一方的<set /> ,可以在 many的一方配置 lazy=false;

11. 添加消息的功能.(假设用户是唯一的.,如果用户不唯一,我们可以通过id来发送信息)

 

 

 

* 在jsp页面中获取当前项目名称可以

<%=this.getServletContext().getContextPath() %>

通过标签: ${pageContext.request.contextPath}

 

 

 

 

 

1.    懒加载是什么? 为什么需要懒加载?

 

2.    如何解决懒加载问题

 

u  为什么需要缓存?

看一个案例:->原理图

从上图看出: 当我们去查询对象的时候,首先到一级缓存去取数据,如果有,则不到数据库中取,如果没有则到数据库中取,同时在一级缓存中放入对象.

u  一级缓存的细节

①     什么操作会向一级缓存放入数据

save,update,saveOrUpdate,load,get,list,iterate,lock

save 案例:

       //添加一个学生

                     Studentstudent=new Student();

                     student.setName("小东");

                    

                     s.save(student);//放入一级缓存

                    

                     //我马上查询

                     Studentstu2=(Student) s.get(Student.class, student.getId()); //select

                     System.out.println("你刚刚加入的学生名字是"+stu2.getName());

②     什么操作会从一级缓存取数据.

get / load / list

 

get / load 会首先从一级缓存中取,如没有.再有不同的操作[get 会立即向数据库发请求,而load 会返回一个代理对象,直到用户真的去使用数据,才会向数据库发请求]

?list 会不会从session缓存取数据?

案例:

//查询45号学生

             

                     Studentstu=(Student) s.get(Student.class, 45);

                    

                     System.out.println("|||||||||||||||||||");

                    

                     Stringhql="from Student where id=45";

                    

                    

                     Studentstu2=(Student) s.createQuery(hql).uniqueResult();

                    

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

从上面的案例,我看出query.list() query.uniueResut() 不会从一级缓取数据! 但是query.list 或者query.uniqueRestu() 会向一级缓存放数据的.

③     一级缓存不需要配置,就可以使用,它本身没有保护机制,所以我们程序员要考虑这个问题,我们可以同 evict 或者 clear来清除session缓存中对象. evict 是清除一个对象,clear是清除所有的sesion缓存对象

④     session级缓存中对象的生命周期, 当session关闭后,就自动销毁.

⑤     我们自己用HashMap来模拟一个Session缓存,加深对缓存的深入.

package com.hsp.view;

 

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

public class MyCache {

       //使用map来模拟缓存

       staticMap<Integer,Student> maps=new HashMap<Integer,Student>();

 

       publicstatic void main(String[] args) {

              // TODO Auto-generated method stub

 

              getStudent(1);

              getStudent(1);

              getStudent(1);

              getStudent(1);

              getStudent(3);

              getStudent(3);

             

             

             

       }

      

       public static Student getStudent(Integerid){  //s.get()

             

             

              //先到缓存去

              if(maps.containsKey(id)){

                     //在缓存有

                     System.out.println("从缓存取出");

                     returnmaps.get(id);

              }else{

                     System.out.println("从数据库中取");

                     //到数据库取

                     Studentstu=MyDB.getStudentFromDB(id);

                     //放入缓存

                     maps.put(id,stu);

                     returnstu;

              }

             

      

             

       }

 

}

 

//我的数据库

class MyDB{

      

       staticList<Student> lists=new ArrayList<Student>();

      

       //初始化数据库,假设有三个学生

       static{

              Student s1=new Student();

              s1.setId(1);

              s1.setName("aaa");

              Student s2=new Student();

              s2.setId(2);

              s2.setName("bbb");

              Student s3=new Student();

              s3.setId(3);

              s3.setName("ccc");

              lists.add(s1);

              lists.add(s2);

              lists.add(s3);

             

       }

      

       publicstatic Student getStudentFromDB(Integer id){

              for(Student s: lists){

                     if(s.getId().equals(id)){

                            return s;

                     }

              }

              return null;// 在数据库中没有.

             

       }

}

 

class Student{

       privateInteger id;

       privateString name;

       publicInteger getId() {

              return id;

       }

       publicvoid setId(Integer id) {

              this.id = id;

       }

       publicString getName() {

              return name;

       }

       publicvoid setName(String name) {

              this.name = name;

       }

}

 

为什么需要二级缓存?

因为一级缓存有限(生命周期短),所以我们需要二级缓存(SessionFactory缓存)来弥补这个问题

1.    需要配置

2.    二级缓存是交给第三方去处理,常见的Hashtable ,OSCache , EHCache

3.    二级缓存的原理

4.    二级缓存的对象可能放在内存,也可能放在磁盘.

 

u  快速入门案例

使用OsCache来演示二级缓存的使用.

 

1.    配置二级缓存

 

对配置说明:

<propertyname="hbm2ddl.auto">update</property>

       <!--启动二级缓存 -->

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

       <!--指定使用哪种二级缓存-->

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

       <mappingresource="com/hsp/domain/Department.hbm.xml" />

       <mappingresource="com/hsp/domain/Student.hbm.xml" />

       <!--指定哪个domain启用二级缓存

       特别说明二级缓存策略:

       1.read-only

       2.read-write

       3.nonstrict-read-write

       4.transcational

       -->

       <class-cache class="com.hsp.domain.Student"usage="read-write"/>

2. 可以文件放在 src目录下,这样你可以指定放入二级缓存的对象capacity 大小. 默认1000

3 使用

// TODO Auto-generated method stub

              //通过获取一个sesion,让hibernate框架运行(config->加载hibernate.cfg.xml)

              Session s=null;

              Transaction tx=null;

             

              try {

                     //我们使用基础模板来讲解.

                     s=HibernateUtil.openSession();

                     tx=s.beginTransaction();

                    

                     //查询45号学生

             

                     Studentstu1=(Student) s.get(Student.class, 45);//45->一级缓存           

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

                    

                    

                     tx.commit();

                    

              } catch (Exception e) {

                     e.printStackTrace();

                     if(tx!=null){

                            tx.rollback();

                     }

              }finally{

                    

                     if(s!=null&& s.isOpen()){

                            s.close();

                     }

              }

             

              System.out.println("*********************************");

              try {

                     //我们使用基础模板来讲解.

                     s=HibernateUtil.openSession();

                     tx=s.beginTransaction();

                    

                     //查询45号学生

             

                     Studentstu1=(Student) s.get(Student.class, 45);    

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

                    

                     Studentstu3=(Student) s.get(Student.class, 46);    

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

                            tx.commit();

                    

              } catch (Exception e) {

                     e.printStackTrace();

                     if(tx!=null){

                            tx.rollback();

                     }

              }finally{

                    

                     if(s!=null&& s.isOpen()){

                            s.close();

                     }

              }

             

              //完成一个统计,统计的信息在Sessfactory

              //SessionFactory对象.

              Statistics statistics=HibernateUtil.getSessionFactory().getStatistics();

              System.out.println(statistics);

              System.out.println("放入"+statistics.getSecondLevelCachePutCount());

              System.out.println("命中"+statistics.getSecondLevelCacheHitCount());

              System.out.println("错过"+statistics.getSecondLevelCacheMissCount());

3. 在配置了二级缓存后,请大家要注意可以通过Statistics,查看你的配置命中率高不高

 

 

主键增长策略

①     increment

自增,每次增长1, 适用于所有数据库. 但是不要使用在多进程,主键类型是数值型

select max(id) from Student

②   identity

自增,每次增长1, 适用于支持identity的数据(mysql,sqlserver), 主键类型是数值

③   sequence

④   native

会根据数据类型来选择,使用identity,sequence ,hilo

selecthibernate_sequence.nextval from dual

主键类型是数值long , short ,int

<idname="id" type="java.lang.Integer">

<generatorclass="native"/>

</id>

⑤   hilo

hilo标识符生成器由Hibernate按照一种high/low算法生成标识符

用法:

<id name=”id” type=”java.lang.Integer” column=”ID”>

    <generatorclass=”hilo”>

   <paramname=”table”>my_hi_value</param>

   <paramname=”column”>next_value</param>

    </generator>

    </id>

⑥   uuid

会根据uuid算法,生成128-bit的字串

主键属性类型不能是数值型,而是字串型

⑦   assigned

用户自己设置主键值,所以主键属性类型可以是数值,字串

⑧   映射复合主键

⑨   foreign

one-to-one的关系中,有另一张表的主键(Person) 来决定自己主键/外键( IdCard)

 

给出一个简单原则:

针对oracle [主键是int/long/short 建议使用 sequence] 主键是String 使用uuid或者assinged

针对 mysql [主键是 int/long/short 建议使用increment/assigend,如果是字串 UUId/assigned]

针对 sql server [主键是 int/long/short 建议使用identity/native/assinged ,如果主键是字串,使用uuid/assigned ]

 

one-to-one 又是基于主键的则使用foreign

 

hibernate最佳实践(在什么项目中使用最好)

对于数据量大,性能要求高系统,不太使用使用hiberante.

主要用于事务操作比较多的项目(oa/某个行业软件[石油、税务、crm, 财务系统.]

 

olap->hibernate用的比较少  oltp->hibernate

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值