介绍
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。
Hibernate映射(ORM对象关系映射)
多和一的映射
<!-- 员工表的映射配置 -->
<hibernate-mapping package="cn.hpe.po" >
<class name="Employee" table="t_employee">
<!-- 主键 -->
<id name="empId" column="empid" >
<!-- 如果此处class设置伟sequence或者native,那么实体类的主键属性必须时int,若是String类型,class必须是uuid -->
<generator class="sequence">
<param name="sequence">seq_emp_empid</param>
</generator>
</id>
<property name="empName" length="50"></property>
<property name="salary" type="double"></property>
<!-- 多对一 映射的另外一张t_dept表 ,dept_id 和 dept表的 映射的外键key标签的属性都是dept_id -->
<many-to-one name="dept" class="Dept" column="dept_id" ></many-to-one>
</class>
</hibernate-mapping>
<!-- 部门表的 映射配置 -->
<hibernate-mapping package="cn.hpe.po" auto-import="true">
<class name="Dept" table="t_dept">
<!-- 主键 -->
<id name="deptId" column="deptid" >
<!-- 如果此处class设置伟sequence或者native,那么实体类的主键属性必须时int,若是String类型,class必须是uuid -->
<generator class="sequence" >
<param name="sequence">seq_dept_deptid</param>
</generator>
</id>
<property name="deptName" length="50"></property>
<!--
set 集合属性映射
name 指定要映射的set集合的属性
table 集合属性要映射到的表
key 指定集合表(t_employee)的外键字段
element 指定集合表的其他字段
type 元素类型,一定要指定
-->
<!--
一对多 映射的另外一张t_employee表 ;
inverse默认false,不控制反转,说明当前方具有控制权;
inverse=true,说明没有控制权,没有控制权不能进行关系解除和删除数据
cascade的值 :none(默认),(save-update),(seve-update,delete 一定要有 逗号),(all 所有但不包括默认none)
cascade=none,默认不级联操作,所谓级联就是操作某个表数据同时也会操作另外关联的表的数据
面试题:inverse和cascade的关系: 当设置inverse=true时cascade也可以进行级联删除
-->
<set name="emps" table="t_employee" inverse="false" cascade="none">
<!-- 配置外键字段 -->
<key column="dept_id"></key>
<one-to-many class="Employee"/>
</set>
</class>
</hibernate-mapping>
一和一的映射
<!-- 身份证表映射 users 的 映射配置 -->
<hibernate-mapping package="cn.hpe.po">
<class name="IdCard" table="t_idcard">
<!-- 主键 -->
<id name="cardNum" column="cardid" >
<!-- 如果此处class设置assigned 指定主键生成策略,为手动指定主键的值 -->
<generator class="assigned"></generator>
</id>
<property name="place" length="100"></property>
<!-- 特殊的多对一映射,即一对一,有外键方,unique=true,设置外键唯一,此时外键也成了'主键 ',此配置是基于外键的配置-->
<many-to-one name="users" class="Users" column="usersid" unique="true" cascade="save-update"/>
</class>
</hibernate-mapping>
<!-- users用户信息的 映射配置 -->
<hibernate-mapping package="cn.hpe.po">
<class name="Users" table="t_users">
<!-- 主键 -->
<id name="usersId" column="usersid" >
<generator class="sequence">
<param name="sequence">seq_tusers_usersid</param>
</generator>
</id>
<property name="userName" length="50"></property>
<!-- 一对一映射,没有外键方 -->
<one-to-one name="idCard1" class="IdCard1" />
</class>
</hibernate-mapping>
多和多的映射
<!-- 项目表的 映射配置 -->
<hibernate-mapping package="cn.hpe.po">
<class name="Project" table="t_project">
<!-- 主键 -->
<id name="prjId" column="prj_id" >
<!-- 如果此处class设置伟sequence或者native,那么实体类的主键属性必须时int,若是String类型,class必须是uuid -->
<generator class="sequence">
<param name="sequence">seq_project_prjid</param>
</generator>
</id>
<property name="prjName" length="50"></property>
<!-- 多对多 映射的另外一张t_relation表 -->
<set name="developers" table="t_relation" cascade="save-update">
<!-- 配置外键字段 -->
<key column="prj_id"></key>
<many-to-many class="Developer" column="d_id" ></many-to-many>
</set>
</class>
</hibernate-mapping>
<!-- 开发人员表的 映射配置 -->
<hibernate-mapping package="cn.hpe.po">
<class name="Developer" table="t_developer">
<!-- 主键 -->
<id name="dId" column="d_id" >
<!-- 如果此处class设置伟sequence或者native,那么实体类的主键属性必须时int,若是String类型,class必须是uuid -->
<generator class="sequence">
<param name="sequence">seq_developer_did</param>
</generator>
</id>
<property name="dName" length="50"></property>
<!--
set 集合属性映射
name 指定要映射的set集合的属性
table 集合属性要映射到的表
key 指定集合表(t_employee)的外键字段
element 指定集合表的其他字段
type 元素类型,一定要指定
-->
<!--
多对多 映射的另外一张t_relation表 ;
inverse默认false,不控制反转,说明当前方具有控制权;
inverse=true,说明没有控制权,没有控制权不能进行关系解除和删除数据
cascade的值 :none(默认),(save-update),(seve-update,delete 一定要有 逗号),(all 所有但不包括默认none)
cascade=none,默认不级联操作,所谓级联就是操作某个表数据同时也会操作另外关联的表的数据
懒加载属性:lazy值:
默认 true 设置懒加载
false 关闭懒加载
extra (更高级,经常使用这个属性)只有在使用的时候才去数据库取数据,防止只查询记录数的时候会把所有的数据查出来
目的:提高效率
-->
<set name="projects" table="t_relation" cascade="save-update" lazy="extra">
<!-- 配置外键字段 -->
<key column="d_id"></key>
<many-to-many class="Project" column="prj_id" ></many-to-many>
</set>
</class>
</hibernate-mapping>
懒加载
及时加载--get()
Developer developer = new Developer();
// Hibernate: select developer0_.d_id as d1_0_0_, developer0_.dName as
// dName0_0_ from t_developer developer0_ where developer0_.d_id=?
// developer = (Developer) session.get(Developer.class, 1);
// get及时加载,会把所有相关的字段查出来
懒加载--load()
// 懒加载,内部hibernate其实使用代理对象($Proxy01)进行懒加载,配置lazy=true。只是加载并没有执行数据库查询,没有sql语句输出
developer = (Developer) session.load(Developer.class, 1);
// 用到时才会查询数据库
// Hibernate: select developer0_.d_id as d1_0_0_, developer0_.dName as
// dName0_0_ from t_developer developer0_ where developer0_.d_id=?
// 用到时才会查询数据库,输出:李四
// System.out.println(developer.getdName());
如何解决在session关闭后输出懒加载内容异常
// 方式1: 先使用一下数据
// developer.getdName();
// 方式2:强迫代理对象初始化
Hibernate.initialize(developer);
// 方式3:关闭懒加载 在映射文件中设置lazy="false"
// 获取事务并提交,这才是执行数据库操作
session.getTransaction().commit();
session.close();
// 如何解决在session关闭后输出懒加载内容异常
System.out.println(developer.getdName());
一级缓存
Developer developer = null;
developer = (Developer) session.get(Developer.class, 1);// 查询数据时先检查缓存中是否有数据,没有数据再从数据库查询
developer = (Developer) session.get(Developer.class, 1);// 此时一级缓存中已经有数据
// 查询数据时先检查缓存中是否有数据,没有数据再从数据库查询
// 获取事务并提交,这才是执行数据库操作
session.getTransaction().commit();
session.close();
// 执行一条sql语句,说明第一次查询时缓存中没有数据,然后放入缓存,接下来的又一次查询会直接从缓存中获取
// Hibernate:select developer0_.d_id as d1_0_0_, developer0_.dName as
// dName0_0_ from t_developer developer0_ where developer0_.d_id=?
测试缓存的方法有:
session.flush()//缓存中的数据同步到数据库中
session.clear()//清空当前缓存
session.evict(developer);// 清空指定对象object
二级缓存
使用二级缓存的前提是在配置文件配置二级缓存,并指定哪些类会加入二级缓存
<!-- ****************** 【二级缓存配置】 ****************** -->
<!-- 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- 开启哪一个缓存框架 -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
<!-- 开启二级缓存中的缓存查询 -->
<property name="hibernate.cache.use_query_cache">true</property>
指定哪一些(常用的)类,需要加入二级缓存,二级缓存会消耗内存,查询速度快
usage缓存策略:
<class-cache usage="read-write" class="cn.hpe.po.Dept"/>
<!-- employee 集合也放入二级缓存查询 -->
<class-cache usage="read-only" class="cn.hpe.po.Employee"/>
<!-- 集合缓存[集合缓存的 元素 对象,也加入二级缓存,就是class="cn.hpe.po.Employee"] -->
<collection-cache usage="read-write" collection="cn.hpe.po.Dept.emps"/>
Dept dept = (Dept) session1.get(Dept.class, 1);//第一次查询,已经放入二级缓存
...
dept = (Dept) session2.get(Dept.class, 1);//另一个session来获取二级缓存的数据,不会从数据库查询
...
mappingLocations,mappingDirectoryLocations,
mappingJarLocations区别
由于spring对hibernate配置文件hibernate.cfg.xml的集成相当好,所以,在项目中我一直使用
spring的org.springframework.orm.hibernate.LocalSessionFactoryBean来取代hibernate.cfg.xml文件的功能
LocalSessionFactoryBean有好几个属性用来查找hibernate映射文件:mappingResources、mappingLocations、
mappingDirectoryLocations与mappingJarLocations
他们的区别:
mappingResources:指定classpath下具体映射文件名
<property name="mappingResources">
<value>petclinic.hbm.xml </value>
</property>
mappingLocations:可以指定任何文件路径,并且可以指定前缀:classpath、file等
<property name="mappingLocations">
<value>/WEB-INF/petclinic.hbm.xml </value>
</property>
<property name="mappingLocations">
<value>classpath:/com/company/domain/petclinic.hbm.xml </value>
</property>
也可以用通配符指定,'*'指定一个文件(路径)名,'**'指定多个文件(路径)名,例如:
<property name="mappingLocations">
<value>classpath:/com/company/domainmaps/*.hbm.xml </value>
</property>
上面的配置是在com/company/domain包下任何maps路径下的hbm.xml文件都被加载为映射文件
mappingDirectoryLocations:指定映射的文件路径
<property name="mappingDirectoryLocations">
<list>
<value>WEB-INF/HibernateMappings</value>
</list>
</property>
也可以通过classpath来指出
<property name="mappingDirectoryLocations">
<list>
<value>classpath:/XXX/package/</value>
</list>
</property>
mappingJarLocations:指定加载的映射文件在jar文件中
关于java <action name="emp-*" class="employeeAction" method="{1}">
这是利用Struts2里面的自动匹配特性。代表通配符,可以匹配任何一个字符串[1]表示匹配的参数。举个例子来说,如果你在xml文件中做了这个配置,那么当你页面里面有一个form
<form action="/emp-add" method="post">
那么,匹配到的就是“add”,同时method="{1}",实际上就是method="add"。
假设有两个参数,我么在xml里面的配置是
<action name="emp-*-*" class="{1}Action" method="{2}">
那么第一个星号对应的是{1},第二个星号对应的是{2}
例如,页面里面有<form action="/emp-employee-add" method="post">
那么实际上的意思就是
<action name="emp-employee-*" class="employeeAction" method="add">
这样做的好处就是我们不必为增删改查资格方法写四个<action>配置。同样如果有相似的逻辑,比如学生信息的增删改查和老师信息的增删改查逻辑是一样的,那么我们也可以通过通配符来节省xml文件中的代码量
前提是一定要遵守通配符的约定
struts运行机制(用户访问一个url)
index.jsp:
<c:redirect url="/employee_list"></c:redirect>
注意url
struts.xml配置
<!-- ### action 实例交给spring创建;链接传过来的地址在这进行处理 ,
method="{1}"就是匹配 ‘*’,就是方法的名字,如果url="/employee_list",则method=list,
自动去class="employeeAction"(action实例),去类 EmployeeAction中找list()方法,执行后方法返回"list"字符串,对应name="list",
匹配后进入/WEB-INF/list.jsp-->
<action name="employee_*" class="employeeAction" method="{1}">//这行代码是第三步随后进入action类里执行后还返回这里才往下执行result name="list">/WEB-INF/list.jsp</result>
<!-- 1.列表展示,放在/WEB-INF 下面 -->
<result name="list">/WEB-INF/list.jsp</result>
<!-- 2.进入添加页面,放在/WEB-INF 下面 -->
<result name="add">/WEB-INF/add.jsp</result>
<!-- 3.添加成功,进入列表,(防止用户刷新多一条记录) -->
<result name="listAction" type="redirect">employee_list</result>
<!-- 4.修改员工数据,进入修改页面 -->
<result name="edit">/WEB-INF/edit.jsp</result>
</action>
EmployeeAction.java
/**
* 1. 员工列表展示
*/
public String list() {
List<Employee> list = employeeService.getAll();
request.put("listEmp", list);
return "list";//返回到struts.xml中
}
<result name="list">/WEB-INF/list.jsp</result>
进入list.jsp页面
list.jsp显示页面
<table border="1" align="center" width="80%" cellpadding="5" cellspacing="0">
<tr>
<th>序号</th>
<th>员工编号</th>
<th>员工姓名</th>
<th>员工薪水</th>
<th>操作</th>
</tr>
<s:if test="#request.listEmp != null">
<s:iterator var="emp" value="#request.listEmp" status="st">
<tr>
<td> <s:property value="#st.count"/> </td>
<td> <s:property value="#emp.employeeId"/> </td>
<td> <s:property value="#emp.employeeName"/> </td>
<td> <s:property value="#emp.employeeSalary"/> </td>
<td> <s:a href="employee_viewUpdate?employeeId=%{#emp.employeeId}">修改 </s:a>
<s:a href="employee_delete?employeeId=%{#emp.employeeId}">删除 </s:a> </td>
</tr>
</s:iterator>
</s:if>
struts登陆成功后用户信息保存到session用actionContext
ActionContext.getContext().getSession().put("adminInfo",adminInfo);
config the follow in Oracle
Employee.hbm.xml:
<class name="Employee" table="t_employee">
<!-- 主键 -->
<id name="empId" column="empid" type="string">
<generator class="uuid"> (if you set class="native",the type of empId of class Employee must be int)
<param name="sequence">seq_emp_empid</param>
</generator>
</id>
<property name="empName" length="50"></property>
<property name="salary" type="double"></property>
<!-- 多对一 映射的另外一张t_dept表 ,dept_id 和 dept表的 映射的外键key标签的属性都是dept_id -->
<many-to-one name="dept" class="Dept" column="dept_id"></many-to-one>
</class>
Employee:
private String empId;// 员工编号
private String empName;// 姓名
private double salary;// 薪水
// 一个员工对应一个部门
private Dept dept;// 所属部门
if you set the type of empId String in Employee, you will set " class="uuid""
and create table auto primary key: uuid
面试题
-
不同的session是否会共享缓存数据?
不会。
User1 u1 = Session1.get(User.class,1); 把u1对象放入session1的缓存
Session2.update(u1); 把u1放入session2的缓存
U1.setName(‘new Name’);
如果生成2条update sql, 说明不同的session使用不同的缓存区,不能共享。 -
Hibernate --list与iterator查询的区别?
list()
一次把所有的记录都查询出来,
会放入缓存,但不会从缓存中获取数据
Iterator
N+1查询; N表示所有的记录总数
即会先发送一条语句查询所有记录的主键(1),
再根据每一个主键再去数据库查询(N)!
缓存策略
<class-cache usage="read-only"/> 放入二级缓存的对象,只读;
<class-cache usage="nonstrict-read-write"/> 非严格的读写
<class-cache usage="read-write"/> 读写;放入二级缓存的对象可以读、写;
<class-cache usage="transactional"/> (基于事务的策略)
缓存的机制:Map<条件,对象>
hibernate工作流程:
1️⃣ 读取并解析配置文件;
2️⃣ Configuration负责读取并创建映射信息,创建sessionfactory;
3️⃣ SessionFactory负责创建session;
4️⃣ Transaction负责开启事物Transaction;
5️⃣ Query负责执行持久化操作;
6️⃣ Transaction负责提交实物;
7️⃣ 关闭session;
8️⃣ 关闭sessionfactory。
懒加载:代理对象
内部hibernate其实使用代理对象($Proxy01)进行懒加载,配置lazy=true。只是加载并没有执行数据库查询,没有sql语句输出
developer=(Developer)session.load(Developer.class,1);
// 用到时才会查询数据库
//Hibernate: select developer0_.d_id as d1_0_0_, developer0_.dName as
// dName0_0_ from t_developer developer0_ where developer0_.d_id=?
// 用到时才会查询数据库,输出:李四
// System.out.println(developer.getdName());
hibernate分页
publicvoidpage(){
Sessionsession=HibernateUtiles.getSession();
// 开始事务
session.beginTransaction();
Queryquery=session.createQuery("from Employee");
// 总记录数--滚动结果集
ScrollableResultssResults=query.scroll();// 得到滚动的结果集
sResults.last();// 滚动到最后一行
inttotalRecords=sResults.getRowNumber()+1;// 获取总记录数
// 设置分页参数,输出2条记录
query.setFirstResult(2);
query.setMaxResults(3);
// 输出查询
System.out.println(query.list());
System.out.println("记录数"+totalRecords);
// 获取事务并提交,这才是执行数据库操作
session.getTransaction().commit();
session.close();
}
hibernate连接查询--迫切连接
@Test
publicvoidfetch(){
Sessionsession=HibernateUtiles.getSession();
// 开始事务
session.beginTransaction();
// 迫切 内连接,把右表的数据填充到左表,返回的类型就是左表的类型dept
// Query query = session
// .createQuery("fromDeptd inner join fetch d.emps");
// query.list();
// 迫切 左外连接,把右表的数据填充到左表,返回的类型就是左表的类型dept
// Query query =
// session.createQuery("fromDeptd left join fetch d.emps");
// query.list();
网友评论