以留言系统为实例
1 .配置映射文件:首先得引入约束dtd
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
然后<hibernate-mapping></hibernate-mapping>映射标签
<class></class>标签 表示你要创建表的javaBean功能类
<id></id>配置表的主键id<generator class="native"><generator> native :hibernate自动是哪种数据库 然后根据数据库类型进行主键的增长 来保证数据的唯一性
<proterty></proterty>来配置表的字段
<set></set> 一对多是有的标签 来表示那个多的集合
<one-to-one> 一对一
<one-to-many>一对多
<many-to-many>多对多
2.核心配置文件
同样的也要引入约束dtd
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
然后<hibernate-configuration></hibernate-configuration>加载核心文件标签
<session-factory></session-factory>session工厂标签
然后3个模块
1 连接数据库 进行配置
peroperty标签
具体的
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/liuyanban</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"></property>
2.hibernate信息模块
输出日志
输出sql语句:<property name="hibernate.show_sql">true</property>
格式化sql语句 <property name="hibernate.format_sql">true</property>
配置建表信息<property name="hibernate.hbm2ddl.auto">update</property>配置了这个有表就更新没有就创建
配置session线程绑定<property name="hibernate.current_session_context_class">thread</property>
配置数据库方言 <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
3。隐射文件配置
<mapping resource属性/>
2.一对一配置
一对一关联分为主键关联和外键关联
主键关联:不需要添加额外的字段,两个表的主键值一样
外键关联:附表有一个额外的字段和主表相关联 或两个表都有额外字段和其对应的表相关联
<one-to-one name="propertyName" (1) class="ClassName" (2) cascade="cascade_style" (3) constrained="true|false" (4) fetch="join|select" (5) property-ref="propertyNameFromAssociatedClass" (6) access="field|property|ClassName" (7) formula="any SQL expression" (8) lazy="proxy|no-proxy|false" (9) entity-name="EntityName" (10) node="element-name|@attribute-name|element/@attribute|." embed-xml="true|false" foreign-key="foreign_key_name" />
cascade:用于级联操作 crud操作 官方解释:表明操作是否从父对象级联到被关联的对象
constrained(约束):表明该类对应的表对应的数据库表,和被关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。 这个选项影响save()和delete()在级联执行时的先后顺序以及 决定该关联能否被委托(也在schema export tool中被使用).
fetch (可选 - 默认设置为select): 在外连接抓取或者序列选择抓取选择其一.
property-ref:指定关联类的属性名 这个将会和本类的主键相对应,如果没有指定会使用对方关联类的主键
lazy:对延迟查询的高效进行指定 如果为extra为及其懒惰 你要什么就给什么不给多也不给少 ,指定这个查询很高效
access (可选 - 默认是 property): Hibernate用来访问属性的策略。
formula (可选):绝大多数一对一的关联都指向其实体的主键。在一些少见的情况中, 你可能会指向其他的一个或多个字段,或者是一个表达式,这些情况下,你可以用一个SQL公式来表示。 (可以在org.hibernate.test.onetooneformula找到例子)
lazy (可选 - 默认为 proxy): 默认情况下,单点关联是经过代理的。lazy="no-proxy"指定此属性应该在实例变量第一次被访问时应该延迟抓取(fetche lazily)(需要运行时字节码的增强)。 lazy="false"指定此关联总是被预先抓取。注意,如果constrained="false", 不可能使用代理,Hibernate会采取预先抓取!
entity-name (可选): 被关联的类的实体名
1.主键关联的:
例: User和Account,一个用户对应一个账户。
在附表的one-to-one中要写constrained="true" 表示受到约束通过一个外键引用对主键进行约束
User类与配置文件
private Integer id;
private String username;
private String password;
private Account account;
one-to-one中的class属性可以不写,默认Hibernate会使用反正自己去寻找。
<one-to-one name="account" class="piaohan.domain.Account"cascade="all" />
Account类与配置
private Integer id;
private String accountNum;
private Integer money;
private User user;
<id name="id" column="id"><generator class="foreign"><!--本类中想要关联的属性--><param name="property">user</param></generator></id>
<one-to-one name="user" class="" constrained="true"></one-to-one>
表示不添加额外的字段 使两个表的主键值一样 这时两个表的主键的值一样
2.单方外键关联 :
例:一个留言对应一个回复
在留言当中显示其对应的回复
Message
public class Message {
private Integer mid;
private String title;
private String mtext;
private String mtime;
private User user;
private Revert revert;
<many-to-one name="revert" class="" unique="true"></many-to-one>
这里就要问了 为什么是many-to-one呢 这种关联Message 实际上这里是用many-to-one然后用unique来进行限制 限制成一对一的关系
所以一对一的单方外键关联是一个特殊的many-to-one的例子
public class Revert {
private Integer rid;
private String rtext;
private String rtime;
private Message message;
<one-to-one name="message" class="" property-ref="rever"></one-to-one>
配置property-ref值为关联类的属性 其对应本类中的主键值 如果没有配置呢?会使用对方关联类的主键
2.双方外键关联
都是用<many-to-one></many-to-one>所以一对一的双方外键关联是一个特殊的many-to-one的例子
关联类的属性是一样的 都要进行unique的配置
<many-to-one name="account" cascade="all" unique="true"
column="accountid" />
Account配置(类不变)
<many-to-one name="user" unique="true" column="userid"
cascade="all" />
一对多的配置
public User() {
super();
// TODO Auto-generated constructor stub
}
private Integer uid;
private String username;
private String password;
private String email;
private Set<Message> messageset=new HashSet<Message>();
一个用户对应多个留言
<set name="messageset(对应本类中的set集合名)">
//使用户有多个留言的关系 外键
<key column="uid"></key>
<one-to-many name="message" class=""></one-to-many>
</set>
public class Message {
private Integer mid;
private String title;
private String mtext;
private String mtime;
private User user;
private Revert revert;
<many-to-one name="user" class="" column="uid(要与主表当中配置的外键保持一致)"></many-to-one>
5.多对多的配置
通过第三张表来表示两个表之间的关系
private Integer rid;
private String name;
//通过建立set集合来表示一个角色可有对应多个用户
private Set<User> userset=new HashSet<User>();
<set name="userset(类中set集合)" table="userrole(第三张表的名称)" cascade="all">
//user和role创建联系
//外键的名称
<key column="rid"></key>
<many-to-many name="user" class="" column="uid"></many-to-many>
</set>
public class User {
private Integer uid;
private String username;
private String password;
//通过建立set集合来实现 一个用户有多个角色对应
private Set<Role> roleset=new HashSet<Role>();
<set name="roleset" table="(要保持一致)">
//反映本类中的主键在第三张表中的外键
<key column="uid"></key>
<many-to-many name="role" class="" column="rid(与另一张中的配置保持一致)"> </many-to-many>
</set>
Hibernate检索策略
分为立即查询和延迟查询
特点:立即查询执行到这一句时立即发送查询数据库语句进行查询数据库 通过session.get()方法
延迟查询:不会立即查询当到对像中取值才会发送语句 ,查询数据库 通过session.load()方法
延迟又分为类级别延迟和关联级别延迟
类级别:通过sesion.load()来实现
关联级别实现:通过配置映射文件lazy(默认为true) fetch(默认为select)
lazy:true (延迟),false(不延迟),extra(极其懒惰(延迟上升一个等级效率高))
批量抓取问题
查询的数据量比较大产生的问题是 会重复查询相同的语句
通过batch-size值越大性能越高
一级缓存和二级缓存的问题
他们的特点:一级缓存:1.默认是打开的
2.存储的数据必须是持久态
3.适用范围是session的开启到关闭
二级缓存:1.需要手动打开
2.范围是sessionfactory范围
User user=session.get(User.class,1(id值))执行过程
user.setName("ji");
首先会看一级缓存中是否有use内容,如果找不到会查询数据库,得到user对象存入一级缓存和快照区中
执行到第二句时 只改变一级缓存中的值而不该快照区中
执行commit时会做一件事情 比较
如果快照区中的值与一级缓存值不相同那么就更新
如果相同那么就不更新。
Hibernate中的session关闭问题
session相当于jdbc中的connection 所以需要进行关闭
但是由于session要与本地线程进行捆绑来实现其单线程对象的特性
创建工具类
package com.jdztc.util; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; /** * * @author 付鹏鹏 *产生SessionFactory核心工具类 */ public class HibernateUtil { //采用静态代码块的方式 因为静态代码块是类加载的时候运行,且只运行一次 private static Configuration configuration=null; private static SessionFactory sessionFactory=null; private static Session session=null; static{ //加载核心配置文件, 创建对象将文件放入对象当中 configuration=new Configuration(); configuration.configure(); //通过有文件的对象的引用来创建SessionFactory ,根据核心配置文件中的数据库信息 和映射文件来进行表格的创建 sessionFactory= configuration.buildSessionFactory(); //通过getCurrentSession()的方法来得到保证是单线程对象的Session session=sessionFactory.getCurrentSession(); } public static Session gerThreadSession(){ return session; } public static SessionFactory getSessionFactory(){ return sessionFactory; } public static void main(String[] args){ } }
在查询数据库中 HibernateUtil.getSession()时不能关闭因为执行到commit()会自动关闭 如果你还关闭将会报session已经关闭
还有一点:commit也不能随便要到最后关闭 举个例子:
public static void addRevert(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String mid=req.getParameter("uid"); System.out.println("uid +"+mid); String content=req.getParameter("content"); System.out.println("content:"+content); if(content.indexOf(" ")!=-1){ content.replace(" ", "<br>"); } Message message=MessageDao.getMessage(mid); if(message!=null){ //将message与revert相关联 设置revert中的message与message中的revert Revert revert=message.getRevert(); System.out.println("revert:"+revert); if(revert==null){ revert=new Revert(); } Date date=new Date(); DateFormat time1=new SimpleDateFormat("yyyy.MM.dd HH:mm:ss "); String time=time1.format(date); // 设置message中的revert System.out.println("revert2:"+revert); message.setRevert(revert); RevertDao.add(revert, content, time, message); //更新留言 MessageDao.updateMessage(message); //req.getRequestDispatcher("index.jsp").forward(req, resp); }else{ System.out.println("message == null"); } }
Message message=MessageDao.getMessage(mid);通过内容来查询message
getMessage(mid)方法中commit和rollback()都不能有全部删除 因为session已经和本地线程绑定 如果关闭
Message message=MessageDao.getMessage(mid);下面的代码将不能得到session 所以必须到
在 MessageDao.updateMessage(message);方法中commit()和rollback()
在addRevert方法的最后进行关闭 注意之前不能有commit()和rollback()