一、开发流程
1.设计实体类(JavaBean):
注意:
hibernate对实体对象的限制:设计的实体对象必须要有无参的构造方法
2.写映射文件(名称一般为"实体类名.hbm.xml"):
<hibernate-mapping package="实体类全名">
<class name="类名" table="表名" [lazy="false" dynamic-insert="true"]> //name为类名,table为表名(可不写,不写时类名与表名称相同),lazy为延迟检索
<id name="属性名" column="主键列名" type="列类型" [unsaved-value="-1"]> //name为类中属性,column为表列名(可不写,同上),unsaved-value用于判断id是否为-1
<generator class="native或identiry"/> //class为hibernate生成器,native或increment为主键生成器(自增)
</id>
<property name="属性名" column="列名" type="列类型" [unique="true" access="property|field" update="false"] insert="false"/> //unique指定该列中的值是否唯一,access指定访问方式(field为访问实体类的变量,property为访问实体类方法,即属性)
或者:
<property name="属性名" type="列类型">
<column name="列名" sql-type="sql类型"> //显式指定表列的sql类型
</property>
</class>
</hibernate-mapping>
3.配置hibernate.cfg.xml文件(一般不要改文件名):
<hibernate-configuration>
<session-factory>
//连接数据库的驱动
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
//连接的url
<property name="connection.url">jdbc:mysql:///数据库名</property>
//用户名
<property name="connection.username">root</property>
//密码
<property name="connection.password">root</property>
//指定连接哪种类型的数据库
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
//设置操作数据库方式,update方式只更新数据库中的表,不删除表,其他的都先删除原表再建新表
<property name="hbm2ddl.auto">create | update | none | create-drop</property>
//用于显示hibernate编写的sql语句(不用设置)
<property name="show_sql">true</property>
//配置映射文件所在路径
<mapping resource="映射文件路径"/>
</session-factory>
</hibernate-configuration>
3.编写工具类代码(以下类全为hibernate包中的类,放在静态中是为了只加载一次,提高性能):
private static SessionFactory sf = null;
static{
方法链编程:
sf = new Configuration().addClass(类1.class)
.addClass(类2.class)
.addClass(类3.class)
.buildSessionFactory();
普通方式:
Configuration conf = new Configuration();
conf.configure(): //读取配置文件
或者:
conf.configure("文件路径"); //当hibernate.cfg.xml文件名被改过后要这么设置
或者:
conf.addClass(类.class); //加载类的映射文件,该文件要与类放一个目录下才能用此方法
或者:
conf.addResource("文件路径");//映射文件名与类名不一样时用这方法
sf = conf.buildSessionFactory(): //构造一个Session工厂,用来new一个Session类
}
Session session = sf.openSession(); //得到一个Session对象,用于返回出去给别的类进行CRUD操作
4.在Dao在写调中代码:
(1)多种查询操作:
按id查询:
实体类 别名 = session.get(实体类.class,id); //根据给出id与实体类型,找出数据库中与实体类对应的数据,返回为实体类
或
实体类 别名 = session.load(实体类.class,id); //和get一样
hql查询:
String hql = "from 实体类 as 别名 where 别名.属性=:引用名";//hql语言查的是实体对象,不是表,只能hibernate中使用
Query query = session.createQuery(hql); //编译hql语句
query.setString(0,值); //和PreparedStatement方法一样,只是这里从0开始
查询多条数据:
实体类 别名 = session.createQuery().uniqueResult(); //查询结果只有一个对象时可以这么用
List list = session.createQuery(hql).list(); //查询结果是一个List集合
查出分页数据:
query.setFirstResult(0): //从0行开始
query.setMaxResults(10); //取10行
按条件查询(criteria)
Criteria c = session.createCriteria(实体类.class);
c.add(Restrictions.eq("属性名",属性值)): //增加条件,判断是否相等
gt("属性名",属性值); //小于
c.list(); //返回List集合
分页:
c.setFirstResult(0); //从0行开始
c.setMaxResults(10): //取10行
(2)增删改操作:
Transaction tran = session.beginTransaction(): //增删改操作必须开启事务
添加方法:
tran.save(实体类); //保存实体类中的信息到数据库中,先保存,失败后回滚
tran.persist(实体类);//同save一样,不同的是当没开启事务时不执行保存操作
添加或更新方法:
session.saveOrUpdate(实体类); //保存或更新,对象变成持久的
session.merge(实体类); //保存或更新,对象还是脱管的
删除方法:
tran.delete(实体类); //删除,会根据id删除
tran.commit(); //提交事务
tran.rollback(); //出现异常时回滚,可以不写,当出异常时系统默认会自动回滚
session.close(); //关闭连接
session.evict(); //将一个对象变为游离状态
session.flush();
session.clear(); //清理
二、映射关系
1.多对一映射:
对象关系:
每一个多方对象都保存着一个单方对象
表关系:
多方表加入一个单方表的主键作为外键,关联单方表
(1)在映射文件中配置
多方表映射文件:
<class name="多方类">
<id name="id">
<generator class="native"/>
</id>
//映射多方表到单方表中的关系,
<many-to-one name="单方类引用变量" column="多方表外键" class="单方类" [cascade="save-update" not-null="true"]/> //not-null设置多方的外键不能为空,cascade为级联操作
</class>
单方表映射文件:
<class name="单方类">
<id name="id">
<generator class="native"/>
</id>
</class>
(2)在Dao中写代码:
保存代码:
//当单方存数据时,第一次多方外键会为空,当单方表有值时,再执行一次更新操作,将单方的主键值存入多方表中的外键中
session.save(单方对象); //当多方表外键不能为空时,单方对象必须先存入
session.save(多方对象);
查询代码:
多方类 别名 = session.get(多方类.class,多方id值); //结果中多对象中会有单方的对象,hibernate会执行两次sql语句
2.一对多映射:
对象关系:
一个单方对象有一个集合保存多个多方对象,一个多方对象保存一个单方对象
表关系:
多方表加入一个单方表的主键作为外键,关联单方表
(1)在映射文件中配置
单方表映射文件:
<class name="单方类">
<id name="id">
<generator class="native"/>
</id>
//将单方类中集合映射到表中,cascade属性执行级联操作,当单方表修改时,多方表也作修改操作
//delete在删除单方时也删除多方表,all-delete-orphan
<set name="单方类中集合引用" order-by="要排序的列名 asc或desc" cascade="save-update,delete,all-delete-orphan" [inverse="true" lazy="false"]> //inverse让单方表放弃维护之间的关系
<key column="多方表外键"/>
<one-to-many class="多方类"/>
</set>
如果用的是List集合,则这样配:
<list name="单方类中集合引用"> //将单方类中集合映射到表中
<key column="多方表外键"/>
<list-index column="列名"/> //用来在表中记录第几个员工
<one-to-many class="多方类"/>
</list>
或
<bag name="单方类中集合引用">
<key column="多方表外键">
<one-to-many class="多方类"/>
</bag>
如果用的是Map集合,则这样配:
<map name="单方类中集合引用"> //将单方类中集合映射到表中
<key column="多方表外键"/>
<map-key type="string" column="多方表列名"> //这个列记录每个员工的在map中的key
<one-to-many class="多方类"/>
</map>
</class>
多方表映射文件:
同多对一相同
(2)在Dao中写代码:
保存代码:
session.save(单方对象); //必须设置cascade属性,不然会报错,多对一关系也会这样
session.save(多方对象1);
session.save(多方对象2);
或
session.save(多方对象1);
session.save(多方对象2);
session.save(单方对象);
查询代码:
单方类 别名 = session.get(单方类.class,单方类id); //会查出所有单方类所应有的多方类,并存入单方类集合属性中
3.一对一映射:
对象关系:
一个主对象中保存着一个副对象,一个副对象中也保存着一个主对象
表关系:
第一种方法:
在副表中id即是主键又是外键,将副表id与主表id进行了外键约束
第二种方法:
副表中增加一个唯一的外键,关联主表id
(1)在映射文件中配置
主表映射文件:
第一种方法:
<class name="主类">
<id name="id">
<generator class="native"/>
</id>
<one-to-one name="主类属性名(副类引用)" class="副类全名" [cascade="save-update,delete"]/> //主对象中的保存的副对象的引用名,即主对象的属性名,cascade级联操作表的方式
</class>
第二种方法:
<class name="主类">
<id name="id">
<generator class="native"/>
</id>
<one-to-one name="主类属性名(到副类引用)" property-ref="副对象属性名(主类引用)"/> //副对象属性名==副对象中保存的主对象
</class>
副表映射文件:
第一种方法:
<class name="副类">
<id name="id">
//此标签指明副表主键id从主对象中取得
<generator class="foreign">
<param name="property">副类属性名(到主类引用)</param> //副类属性名就是保存着主对象的属性
</generator>
</id>
<one-to-one name="副类属性名(到主类引用)" class="主类全名" constrained="true"/> constrained为副表增加到主表的外键约束
</class>
第二种方法:
<class name="副类">
<id name="id">
<generator class="native"/>
</id>
<many-to-one name="主表名" column="副表外键" unique="true"/> //用一个单独的列作为外键关联主表,unique指明外键唯一的
</class>
(2)在Dao中写代码:
保存代码:
只要写保存副对象就能将主对象也保存了:
session.save(副对象);
两个对象都保存也一样效果,前后顺序没关系,都执行了两条插入语句:
session.save(主对象);
session.save(副对象);
或
session.save(副对象);
session.save(主对象);
session
查询代码:
主对象 别名 = session.get(主对象.class,主对象id); //查询用了两表联查,用了一句,给副类也赋值了
或
副对象 别名 = session.get(副对象.class,副对象id); //也会查出主类的值,只是要用两条查询语句
4.多对多映射:
(1)对象关系:
A对象中用一个集合保存着多个B对象
B对象中也用一个集合保存着多个A对象
表关系:
利用第三张表来存放A表与B表的id
(2)在映射文件中配置:
A类映射文件:
<class name="A类名">
<id name="id">
<generator class="native"/>
</id>
<set name="A类中的集合别名" table="连接AB表的中间表名" inverse="true"> //集合中保存的是多个B对象,要在其中一方配inverse属性,让一方放弃对关系的维护,否则会出会重插操作,会出错
<key column="中间表列名1"/> //此id对应A表id
<many-to-many class="B类全名" column="中间表列名2"/> //此id对应B表id
</set>
</class>
B类映射文件:
<class name=B类名">
<id name="id">
<generator class="native"/>
</id>
<set name="B类中的集合别名" table="连接AB表的中间表名"> //集合中保存的是多个A对象
<key column="中间表列名1"/> //此id对应B表id
<many-to-many class="A类全名" column="中间表列名2"/> //此id对应A表id
</set>
</class>
(3)在Dao中写代码:
保存代码:
session.save(A对象); //前后互换效果一样,执行的sql语句也一样
session.save(B对象);
查询代码:
A对象 别名 = session.get(A对象.class,A对象id); //会将A对象中集合中所需的值也查出来的
5.组件映射:
(1)对象关系:
整体对象中,有一个用于保存着一个组件的对象属性
组件对象中无整体对象的属性
(2)表关系:
两个关联对象保存在一张表
(3)在映射文件中配置:
整体类映射文件:
<class name="整体类名" table="表名">
<id name="id">
<generator class="native"/>
</id>
<component name="到组件对象的引用" class="组件类全名"> //name就是整体类中保存的组件对象
<property name="组件类属性1" column="列名1" type="列类型"/>
<property name="组件类属性2" column="列名2" type="列类型"/>
</component>
</class>
6.影射继承关系:
(1)对象关系(三种方法一样):
父类,有id及其他属性
子类,要继承父类,无id属性,只有其他属性,可以有多个子类
(2)表关系(父子表的id值不会重复):
方法一(single):
父子类全合并在一张表中
方法二(joined):
父表与每个子类都是一对一
父表,有父类属性列,几个子类,就产生几个只有id值的null行,供子表id取值
子表,id列+子类属性列
方法三(union):
一个类一张表,每张表都是完整的,不存在父子表关系
父表,父类属性
子表,父类+子类属性
(3)在映射文件中配置(只需一个父类映射文件):
方法一(single):
<class name="父类全名" table="表" discriminator-value="分隔列值">
<id name="id属性" column="id列名" type="表列类型">
<generator class="native" />
</id>
<discriminator column="列名" type="类型"/> //增加分隔列,区分父子类数据(必须加这行)
<property name="父类属性" column="列名" type="表列类型"/>
<subclass name="子类全名" discriminator-value="分隔列值"> //分隔列值会显示在分隔列中(可以没有,没有指定该值,会将子类全名作为值)
<property name="子类属性" column="列名" type="表列类型"/>
</subclass>
</class>
方法二(joined):
<class name="父类全名" table="父表">
<id name="父类id属性" column="父表id列名" type="表列类型">
<generator class="native" />
</id>
<property name="父类属性" column="父表列名" type="表列类型"/>
<joined-subclass name="子类全名" table="子表">
<key column="子表id列名"/> //此id为子表id列,值从父类id中取得
<property name="子类属性" column="子表列名" type="表列类型"/>
</joined-subclass>
</class>
方法三(union):
<class name="父类全名" table="父表">
<id name="父类id属性" column="父表id列名" type="表列类型">
<generator class="hilo">
<param name="table">表名</param> //创建一个产生id值的表
<param name="column">列名</param> //该列存放产生id次数
<param name="max_lo">10</param> //设置每次产生id步长
</generator>
</id>
<property name="父类属性" column="父表列名" type="表列类型"/>
<union-subclass name="子类全名" table="子表">
<property name="子类属性" column="子表列名" type="表列类型"/>
</union-subclass>
</class>
三、控制hibernate查询时的检索方式:
可以用在class、set、many-to-one标签上
1.fetch:
fetch="join" //用迫切左外连接的方式查询
fetch="select" //让lazy选择
fetch="subselect" //用嵌套的子查询方式查询
2.lazy:
lazy= true | false //开启懒加载 | 关闭懒加载
lazy= extra //开启非常懒加载,只查指定数据
lazy= proxy | noproxy //使用按类级别的设置 | 不使用类级别的设置
3.batch-size:
batch-size="数值" //查询多个客户时,一次查询定单集合的数量
四、CRUD方法:
1.Session方法:
s.load(): //加载查询,不存在时抛异常
s.get(); //查询,不存在时返回null
s.save(); //保存
s.saveOrUpdate(): //保存或更新
s.update(); //更新,如果想数据没改变时不进行更新,要在映射文件中:<class name="".. select-before-update="true">
s.delete(); //删除
s.flush(); //数据库和缓存同步,以缓存为准,不提交.
s.refresh(); //缓存和数据库同步,执行查询,更新缓存的对象.
s.clear(); //清空缓存,释放空间
s.createQuery(hql).iterate(); //对结果进行迭代
分组函数:count() sum() max() min() avg()
2.hql语法:
String hql = "hql语句"; //hql与sql基本一样,将sql的表名与列名换成类名与属性名就即可
hql = "from java.lang.Object"; //多态查询,查所有Object类及子类映射的表
hql = "from java.io.Serializable"; //多态查询,查询所有实现该接口的类所对应的表
Querty q = session.createQuery(hql语句); //相当于编译hql语句
q.uniqueResult(); //当只有一条结果时用
q.executeUpdate(); //更新
q.setFirstResult(开始行号).setMaxResults(行数).list(); //分页取数据
q.uniqueResult(); //只有一行数据用
像jdbc中的PreparedStatement同样效果:
按参数名:
q = session.createQuery("from User类 where name属性=:参数名1 and password属性=:参数名2");
q.setString("参数名1","值");
q.setString("参数名2","值");
按索引:
q = session.createQuery("from 类 where 属性1=? and 属性2=?");
q.setString(0,"值"); //hql语句从0开始
q.setString(1,"值");
命名查询:
在类映射文件中class标签后增加:
<query name="参数名">
<![CDATA[from 类 where 属性=?]]>
</query>
在代码中调中此语句:
q = s.getNamedQuery("参数名");
q.setString(0, "值");
3.qbc语法(Criteria,主要用于动态查询):
Criteria crt = session.createCriteria(类.class);
Criterion a1 = Restrictions.eq("属性名","值"); //判断该属性名是否等于该值,相当于where中的=
Criterion a2 = Restrictions.like("属性名","值"); //相当于where中的like
Criterion a3 = Restrictions.and(a1,a2); //相当于sql中的and
Criterion a4 = Restrictions.or(a1,a3); //相当于sql中的or
Criterion a5 = Restrictions.lt(属性,值); //相当于sql中的<
Criterion a6 = Restrictions.ge(属性,值); //相当于sql中的>=
crt.add(a1).list(); //将条件加入查询,得到结果
crt.addOrder(类全名.desc("属性名")).list(); //对结果集降序排列
crt.setFirstResult(开始行号).setMaxResults(行数).list(); //分页取数据
crt.uniqueResult(); //结果只有一行时用
五、在hibernate.cfg.xml文件中的一些配置:
<session-factory>
1.配置c3p0数据源:
导包:c3p0-0.9.1.2.jar
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<property name="c3p0.max_size">最大连接数</property>
<property name="c3p0.min_size">最小连接数</property>
<property name="c3p0.acquire_increment">增量数</property>
2.配置数据库连接的隔离级别:
//隔离级别数:1:读未提交 2:读已提交 4:可重复读 8:串行化(悲观锁) ansi sql
<property name="connection.isolation">hibernate中的数据隔离级别数</property>
3.采用线程本地化技术,为每个线程开启一个独立的session(可防止并发问题,在web中要常用):
<property name="current_session_context_class">thread</property>
在程序中调用:
SessionFactory.getCurrentSession();
</session-factory>
六、配置二级缓存:
用hql语句更新时,会更新二级缓存中的时间戳,当发现数据更新时,会查库,再更新二级缓存的数据。
用对象进行更新时,会一起更新二级缓存中的数据,因此更新完后再查时直接查二级缓存,不查库
1.使用ehcache缓存插件:
ehcache.jar
commons-logging-1.0.4.jar
2.配置缓存供应商(在hibernate.cfg.xml中):
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
3.配置类缓存和集合缓存(在hibernate.cfg.xml中):
<class-cache class="类全名" usage="read-write"/> //类缓存
<collection-cache collection="类全名.集合属性名" usage="read-write"/> //集合缓存
4.创建ehcache.xml(配置缓存有效期,ehcache-1.2.3.jar/ehcache-failsafe.xml中有样文件):
<ehcache>
<diskStore path="java.io.tmpdir"/> //代表系统用户的临时目录
<defaultCache //默认
maxElementsInMemory="10" //内存中元素最大值
eternal="false" //是否过期,false为不过期,true为过期
timeToIdleSeconds="120" //允许空闲时间秒数,超过后缓存失效
timeToLiveSeconds="120" //缓存可用有效秒数
overflowToDisk="true" //如果内存数据溢出,是否将溢出部分存入硬盘临时目录中
diskPersistent="false" //
diskExpiryThreadIntervalSeconds="120" //
memoryStoreEvictionPolicy="LRU"/> //
</ehcache>
以上操作完全成即能使用二级缓存了
5.开或关二级缓存:
<property name="cache.use_second_level_cache">true或false</property> //配好时默认是打开的
6.开启查询缓存(在hibernate.cfg.xml中):
(1)<property name="cache.use_query_cache">true</property>
(2)指定查询结果可以存入二级缓存中:
Query q = session.createQuery(hql);
q.setCacheable(true);
q.list(); //下次同样的查询时,会直接查缓存中的结果,但是也要设setCacheable为true
第二次查:
q = session.createQuery(hql);
q.setCacheable(true); //要设这一句
q.list();
初始化属性:
Hibernate.initialize(属性名);
1.设计实体类(JavaBean):
注意:
hibernate对实体对象的限制:设计的实体对象必须要有无参的构造方法
2.写映射文件(名称一般为"实体类名.hbm.xml"):
<hibernate-mapping package="实体类全名">
<class name="类名" table="表名" [lazy="false" dynamic-insert="true"]> //name为类名,table为表名(可不写,不写时类名与表名称相同),lazy为延迟检索
<id name="属性名" column="主键列名" type="列类型" [unsaved-value="-1"]> //name为类中属性,column为表列名(可不写,同上),unsaved-value用于判断id是否为-1
<generator class="native或identiry"/> //class为hibernate生成器,native或increment为主键生成器(自增)
</id>
<property name="属性名" column="列名" type="列类型" [unique="true" access="property|field" update="false"] insert="false"/> //unique指定该列中的值是否唯一,access指定访问方式(field为访问实体类的变量,property为访问实体类方法,即属性)
或者:
<property name="属性名" type="列类型">
<column name="列名" sql-type="sql类型"> //显式指定表列的sql类型
</property>
</class>
</hibernate-mapping>
3.配置hibernate.cfg.xml文件(一般不要改文件名):
<hibernate-configuration>
<session-factory>
//连接数据库的驱动
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
//连接的url
<property name="connection.url">jdbc:mysql:///数据库名</property>
//用户名
<property name="connection.username">root</property>
//密码
<property name="connection.password">root</property>
//指定连接哪种类型的数据库
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
//设置操作数据库方式,update方式只更新数据库中的表,不删除表,其他的都先删除原表再建新表
<property name="hbm2ddl.auto">create | update | none | create-drop</property>
//用于显示hibernate编写的sql语句(不用设置)
<property name="show_sql">true</property>
//配置映射文件所在路径
<mapping resource="映射文件路径"/>
</session-factory>
</hibernate-configuration>
3.编写工具类代码(以下类全为hibernate包中的类,放在静态中是为了只加载一次,提高性能):
private static SessionFactory sf = null;
static{
方法链编程:
sf = new Configuration().addClass(类1.class)
.addClass(类2.class)
.addClass(类3.class)
.buildSessionFactory();
普通方式:
Configuration conf = new Configuration();
conf.configure(): //读取配置文件
或者:
conf.configure("文件路径"); //当hibernate.cfg.xml文件名被改过后要这么设置
或者:
conf.addClass(类.class); //加载类的映射文件,该文件要与类放一个目录下才能用此方法
或者:
conf.addResource("文件路径");//映射文件名与类名不一样时用这方法
sf = conf.buildSessionFactory(): //构造一个Session工厂,用来new一个Session类
}
Session session = sf.openSession(); //得到一个Session对象,用于返回出去给别的类进行CRUD操作
4.在Dao在写调中代码:
(1)多种查询操作:
按id查询:
实体类 别名 = session.get(实体类.class,id); //根据给出id与实体类型,找出数据库中与实体类对应的数据,返回为实体类
或
实体类 别名 = session.load(实体类.class,id); //和get一样
hql查询:
String hql = "from 实体类 as 别名 where 别名.属性=:引用名";//hql语言查的是实体对象,不是表,只能hibernate中使用
Query query = session.createQuery(hql); //编译hql语句
query.setString(0,值); //和PreparedStatement方法一样,只是这里从0开始
查询多条数据:
实体类 别名 = session.createQuery().uniqueResult(); //查询结果只有一个对象时可以这么用
List list = session.createQuery(hql).list(); //查询结果是一个List集合
查出分页数据:
query.setFirstResult(0): //从0行开始
query.setMaxResults(10); //取10行
按条件查询(criteria)
Criteria c = session.createCriteria(实体类.class);
c.add(Restrictions.eq("属性名",属性值)): //增加条件,判断是否相等
gt("属性名",属性值); //小于
c.list(); //返回List集合
分页:
c.setFirstResult(0); //从0行开始
c.setMaxResults(10): //取10行
(2)增删改操作:
Transaction tran = session.beginTransaction(): //增删改操作必须开启事务
添加方法:
tran.save(实体类); //保存实体类中的信息到数据库中,先保存,失败后回滚
tran.persist(实体类);//同save一样,不同的是当没开启事务时不执行保存操作
添加或更新方法:
session.saveOrUpdate(实体类); //保存或更新,对象变成持久的
session.merge(实体类); //保存或更新,对象还是脱管的
删除方法:
tran.delete(实体类); //删除,会根据id删除
tran.commit(); //提交事务
tran.rollback(); //出现异常时回滚,可以不写,当出异常时系统默认会自动回滚
session.close(); //关闭连接
session.evict(); //将一个对象变为游离状态
session.flush();
session.clear(); //清理
二、映射关系
1.多对一映射:
对象关系:
每一个多方对象都保存着一个单方对象
表关系:
多方表加入一个单方表的主键作为外键,关联单方表
(1)在映射文件中配置
多方表映射文件:
<class name="多方类">
<id name="id">
<generator class="native"/>
</id>
//映射多方表到单方表中的关系,
<many-to-one name="单方类引用变量" column="多方表外键" class="单方类" [cascade="save-update" not-null="true"]/> //not-null设置多方的外键不能为空,cascade为级联操作
</class>
单方表映射文件:
<class name="单方类">
<id name="id">
<generator class="native"/>
</id>
</class>
(2)在Dao中写代码:
保存代码:
//当单方存数据时,第一次多方外键会为空,当单方表有值时,再执行一次更新操作,将单方的主键值存入多方表中的外键中
session.save(单方对象); //当多方表外键不能为空时,单方对象必须先存入
session.save(多方对象);
查询代码:
多方类 别名 = session.get(多方类.class,多方id值); //结果中多对象中会有单方的对象,hibernate会执行两次sql语句
2.一对多映射:
对象关系:
一个单方对象有一个集合保存多个多方对象,一个多方对象保存一个单方对象
表关系:
多方表加入一个单方表的主键作为外键,关联单方表
(1)在映射文件中配置
单方表映射文件:
<class name="单方类">
<id name="id">
<generator class="native"/>
</id>
//将单方类中集合映射到表中,cascade属性执行级联操作,当单方表修改时,多方表也作修改操作
//delete在删除单方时也删除多方表,all-delete-orphan
<set name="单方类中集合引用" order-by="要排序的列名 asc或desc" cascade="save-update,delete,all-delete-orphan" [inverse="true" lazy="false"]> //inverse让单方表放弃维护之间的关系
<key column="多方表外键"/>
<one-to-many class="多方类"/>
</set>
如果用的是List集合,则这样配:
<list name="单方类中集合引用"> //将单方类中集合映射到表中
<key column="多方表外键"/>
<list-index column="列名"/> //用来在表中记录第几个员工
<one-to-many class="多方类"/>
</list>
或
<bag name="单方类中集合引用">
<key column="多方表外键">
<one-to-many class="多方类"/>
</bag>
如果用的是Map集合,则这样配:
<map name="单方类中集合引用"> //将单方类中集合映射到表中
<key column="多方表外键"/>
<map-key type="string" column="多方表列名"> //这个列记录每个员工的在map中的key
<one-to-many class="多方类"/>
</map>
</class>
多方表映射文件:
同多对一相同
(2)在Dao中写代码:
保存代码:
session.save(单方对象); //必须设置cascade属性,不然会报错,多对一关系也会这样
session.save(多方对象1);
session.save(多方对象2);
或
session.save(多方对象1);
session.save(多方对象2);
session.save(单方对象);
查询代码:
单方类 别名 = session.get(单方类.class,单方类id); //会查出所有单方类所应有的多方类,并存入单方类集合属性中
3.一对一映射:
对象关系:
一个主对象中保存着一个副对象,一个副对象中也保存着一个主对象
表关系:
第一种方法:
在副表中id即是主键又是外键,将副表id与主表id进行了外键约束
第二种方法:
副表中增加一个唯一的外键,关联主表id
(1)在映射文件中配置
主表映射文件:
第一种方法:
<class name="主类">
<id name="id">
<generator class="native"/>
</id>
<one-to-one name="主类属性名(副类引用)" class="副类全名" [cascade="save-update,delete"]/> //主对象中的保存的副对象的引用名,即主对象的属性名,cascade级联操作表的方式
</class>
第二种方法:
<class name="主类">
<id name="id">
<generator class="native"/>
</id>
<one-to-one name="主类属性名(到副类引用)" property-ref="副对象属性名(主类引用)"/> //副对象属性名==副对象中保存的主对象
</class>
副表映射文件:
第一种方法:
<class name="副类">
<id name="id">
//此标签指明副表主键id从主对象中取得
<generator class="foreign">
<param name="property">副类属性名(到主类引用)</param> //副类属性名就是保存着主对象的属性
</generator>
</id>
<one-to-one name="副类属性名(到主类引用)" class="主类全名" constrained="true"/> constrained为副表增加到主表的外键约束
</class>
第二种方法:
<class name="副类">
<id name="id">
<generator class="native"/>
</id>
<many-to-one name="主表名" column="副表外键" unique="true"/> //用一个单独的列作为外键关联主表,unique指明外键唯一的
</class>
(2)在Dao中写代码:
保存代码:
只要写保存副对象就能将主对象也保存了:
session.save(副对象);
两个对象都保存也一样效果,前后顺序没关系,都执行了两条插入语句:
session.save(主对象);
session.save(副对象);
或
session.save(副对象);
session.save(主对象);
session
查询代码:
主对象 别名 = session.get(主对象.class,主对象id); //查询用了两表联查,用了一句,给副类也赋值了
或
副对象 别名 = session.get(副对象.class,副对象id); //也会查出主类的值,只是要用两条查询语句
4.多对多映射:
(1)对象关系:
A对象中用一个集合保存着多个B对象
B对象中也用一个集合保存着多个A对象
表关系:
利用第三张表来存放A表与B表的id
(2)在映射文件中配置:
A类映射文件:
<class name="A类名">
<id name="id">
<generator class="native"/>
</id>
<set name="A类中的集合别名" table="连接AB表的中间表名" inverse="true"> //集合中保存的是多个B对象,要在其中一方配inverse属性,让一方放弃对关系的维护,否则会出会重插操作,会出错
<key column="中间表列名1"/> //此id对应A表id
<many-to-many class="B类全名" column="中间表列名2"/> //此id对应B表id
</set>
</class>
B类映射文件:
<class name=B类名">
<id name="id">
<generator class="native"/>
</id>
<set name="B类中的集合别名" table="连接AB表的中间表名"> //集合中保存的是多个A对象
<key column="中间表列名1"/> //此id对应B表id
<many-to-many class="A类全名" column="中间表列名2"/> //此id对应A表id
</set>
</class>
(3)在Dao中写代码:
保存代码:
session.save(A对象); //前后互换效果一样,执行的sql语句也一样
session.save(B对象);
查询代码:
A对象 别名 = session.get(A对象.class,A对象id); //会将A对象中集合中所需的值也查出来的
5.组件映射:
(1)对象关系:
整体对象中,有一个用于保存着一个组件的对象属性
组件对象中无整体对象的属性
(2)表关系:
两个关联对象保存在一张表
(3)在映射文件中配置:
整体类映射文件:
<class name="整体类名" table="表名">
<id name="id">
<generator class="native"/>
</id>
<component name="到组件对象的引用" class="组件类全名"> //name就是整体类中保存的组件对象
<property name="组件类属性1" column="列名1" type="列类型"/>
<property name="组件类属性2" column="列名2" type="列类型"/>
</component>
</class>
6.影射继承关系:
(1)对象关系(三种方法一样):
父类,有id及其他属性
子类,要继承父类,无id属性,只有其他属性,可以有多个子类
(2)表关系(父子表的id值不会重复):
方法一(single):
父子类全合并在一张表中
方法二(joined):
父表与每个子类都是一对一
父表,有父类属性列,几个子类,就产生几个只有id值的null行,供子表id取值
子表,id列+子类属性列
方法三(union):
一个类一张表,每张表都是完整的,不存在父子表关系
父表,父类属性
子表,父类+子类属性
(3)在映射文件中配置(只需一个父类映射文件):
方法一(single):
<class name="父类全名" table="表" discriminator-value="分隔列值">
<id name="id属性" column="id列名" type="表列类型">
<generator class="native" />
</id>
<discriminator column="列名" type="类型"/> //增加分隔列,区分父子类数据(必须加这行)
<property name="父类属性" column="列名" type="表列类型"/>
<subclass name="子类全名" discriminator-value="分隔列值"> //分隔列值会显示在分隔列中(可以没有,没有指定该值,会将子类全名作为值)
<property name="子类属性" column="列名" type="表列类型"/>
</subclass>
</class>
方法二(joined):
<class name="父类全名" table="父表">
<id name="父类id属性" column="父表id列名" type="表列类型">
<generator class="native" />
</id>
<property name="父类属性" column="父表列名" type="表列类型"/>
<joined-subclass name="子类全名" table="子表">
<key column="子表id列名"/> //此id为子表id列,值从父类id中取得
<property name="子类属性" column="子表列名" type="表列类型"/>
</joined-subclass>
</class>
方法三(union):
<class name="父类全名" table="父表">
<id name="父类id属性" column="父表id列名" type="表列类型">
<generator class="hilo">
<param name="table">表名</param> //创建一个产生id值的表
<param name="column">列名</param> //该列存放产生id次数
<param name="max_lo">10</param> //设置每次产生id步长
</generator>
</id>
<property name="父类属性" column="父表列名" type="表列类型"/>
<union-subclass name="子类全名" table="子表">
<property name="子类属性" column="子表列名" type="表列类型"/>
</union-subclass>
</class>
三、控制hibernate查询时的检索方式:
可以用在class、set、many-to-one标签上
1.fetch:
fetch="join" //用迫切左外连接的方式查询
fetch="select" //让lazy选择
fetch="subselect" //用嵌套的子查询方式查询
2.lazy:
lazy= true | false //开启懒加载 | 关闭懒加载
lazy= extra //开启非常懒加载,只查指定数据
lazy= proxy | noproxy //使用按类级别的设置 | 不使用类级别的设置
3.batch-size:
batch-size="数值" //查询多个客户时,一次查询定单集合的数量
四、CRUD方法:
1.Session方法:
s.load(): //加载查询,不存在时抛异常
s.get(); //查询,不存在时返回null
s.save(); //保存
s.saveOrUpdate(): //保存或更新
s.update(); //更新,如果想数据没改变时不进行更新,要在映射文件中:<class name="".. select-before-update="true">
s.delete(); //删除
s.flush(); //数据库和缓存同步,以缓存为准,不提交.
s.refresh(); //缓存和数据库同步,执行查询,更新缓存的对象.
s.clear(); //清空缓存,释放空间
s.createQuery(hql).iterate(); //对结果进行迭代
分组函数:count() sum() max() min() avg()
2.hql语法:
String hql = "hql语句"; //hql与sql基本一样,将sql的表名与列名换成类名与属性名就即可
hql = "from java.lang.Object"; //多态查询,查所有Object类及子类映射的表
hql = "from java.io.Serializable"; //多态查询,查询所有实现该接口的类所对应的表
Querty q = session.createQuery(hql语句); //相当于编译hql语句
q.uniqueResult(); //当只有一条结果时用
q.executeUpdate(); //更新
q.setFirstResult(开始行号).setMaxResults(行数).list(); //分页取数据
q.uniqueResult(); //只有一行数据用
像jdbc中的PreparedStatement同样效果:
按参数名:
q = session.createQuery("from User类 where name属性=:参数名1 and password属性=:参数名2");
q.setString("参数名1","值");
q.setString("参数名2","值");
按索引:
q = session.createQuery("from 类 where 属性1=? and 属性2=?");
q.setString(0,"值"); //hql语句从0开始
q.setString(1,"值");
命名查询:
在类映射文件中class标签后增加:
<query name="参数名">
<![CDATA[from 类 where 属性=?]]>
</query>
在代码中调中此语句:
q = s.getNamedQuery("参数名");
q.setString(0, "值");
3.qbc语法(Criteria,主要用于动态查询):
Criteria crt = session.createCriteria(类.class);
Criterion a1 = Restrictions.eq("属性名","值"); //判断该属性名是否等于该值,相当于where中的=
Criterion a2 = Restrictions.like("属性名","值"); //相当于where中的like
Criterion a3 = Restrictions.and(a1,a2); //相当于sql中的and
Criterion a4 = Restrictions.or(a1,a3); //相当于sql中的or
Criterion a5 = Restrictions.lt(属性,值); //相当于sql中的<
Criterion a6 = Restrictions.ge(属性,值); //相当于sql中的>=
crt.add(a1).list(); //将条件加入查询,得到结果
crt.addOrder(类全名.desc("属性名")).list(); //对结果集降序排列
crt.setFirstResult(开始行号).setMaxResults(行数).list(); //分页取数据
crt.uniqueResult(); //结果只有一行时用
五、在hibernate.cfg.xml文件中的一些配置:
<session-factory>
1.配置c3p0数据源:
导包:c3p0-0.9.1.2.jar
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<property name="c3p0.max_size">最大连接数</property>
<property name="c3p0.min_size">最小连接数</property>
<property name="c3p0.acquire_increment">增量数</property>
2.配置数据库连接的隔离级别:
//隔离级别数:1:读未提交 2:读已提交 4:可重复读 8:串行化(悲观锁) ansi sql
<property name="connection.isolation">hibernate中的数据隔离级别数</property>
3.采用线程本地化技术,为每个线程开启一个独立的session(可防止并发问题,在web中要常用):
<property name="current_session_context_class">thread</property>
在程序中调用:
SessionFactory.getCurrentSession();
</session-factory>
六、配置二级缓存:
用hql语句更新时,会更新二级缓存中的时间戳,当发现数据更新时,会查库,再更新二级缓存的数据。
用对象进行更新时,会一起更新二级缓存中的数据,因此更新完后再查时直接查二级缓存,不查库
1.使用ehcache缓存插件:
ehcache.jar
commons-logging-1.0.4.jar
2.配置缓存供应商(在hibernate.cfg.xml中):
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
3.配置类缓存和集合缓存(在hibernate.cfg.xml中):
<class-cache class="类全名" usage="read-write"/> //类缓存
<collection-cache collection="类全名.集合属性名" usage="read-write"/> //集合缓存
4.创建ehcache.xml(配置缓存有效期,ehcache-1.2.3.jar/ehcache-failsafe.xml中有样文件):
<ehcache>
<diskStore path="java.io.tmpdir"/> //代表系统用户的临时目录
<defaultCache //默认
maxElementsInMemory="10" //内存中元素最大值
eternal="false" //是否过期,false为不过期,true为过期
timeToIdleSeconds="120" //允许空闲时间秒数,超过后缓存失效
timeToLiveSeconds="120" //缓存可用有效秒数
overflowToDisk="true" //如果内存数据溢出,是否将溢出部分存入硬盘临时目录中
diskPersistent="false" //
diskExpiryThreadIntervalSeconds="120" //
memoryStoreEvictionPolicy="LRU"/> //
</ehcache>
以上操作完全成即能使用二级缓存了
5.开或关二级缓存:
<property name="cache.use_second_level_cache">true或false</property> //配好时默认是打开的
6.开启查询缓存(在hibernate.cfg.xml中):
(1)<property name="cache.use_query_cache">true</property>
(2)指定查询结果可以存入二级缓存中:
Query q = session.createQuery(hql);
q.setCacheable(true);
q.list(); //下次同样的查询时,会直接查缓存中的结果,但是也要设setCacheable为true
第二次查:
q = session.createQuery(hql);
q.setCacheable(true); //要设这一句
q.list();
初始化属性:
Hibernate.initialize(属性名);