hibernate
hibernate好处:
- Dao层框架
- 面向对象的思路操作框架
操作数据库的时候,可以以面向对象的方式来完成。不需要写SQL语句;
hibernate:搭建
- 导包
- 创建数据库
- 书写orm元数据(对象与表的映射配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.rabbit.domain.Customer" table="cst_customer">
<id name="cust_id" column="cust_id">
<generator class="native"></generator>
</id>
<property name="cust_name" column="cust_name"></property>
<property name="cust_source" column="cust_source"></property>
<property name="cust_industyr" column="cust_industyr"></property>
<property name="cust_level" column="cust_level"></property>
<property name="cust_linkman" column="cust_linkman"></property>
<property name="cust_phone" column="cust_phone"></property>
<property name="cust_mobile" column="cust_mobile"></property>
</class>
</hibernate-mapping>
- 书写主配置文件:文件名必须为:hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!--
#hibernate.dialect org.hibernate.dialect.MySQLDialect
#hibernate.dialect org.hibernate.dialect.MySQLInnoDBDialect
#hibernate.dialect org.hibernate.dialect.MySQLMyISAMDialect
#hibernate.connection.driver_class com.mysql.jdbc.Driver
#hibernate.connection.url jdbc:mysql:///test
#hibernate.connection.username gavin
#hibernate.connection.password
-->
<property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///mysqltest?serverTimezone=GMT%2B8</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">admin</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- #hibernate.show_sql true
#hibernate.format_sql true
## auto schema export
#hibernate.hbm2ddl.auto create-drop
#hibernate.hbm2ddl.auto create
#hibernate.hbm2ddl.auto update
#hibernate.hbm2ddl.auto validate
-->
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 写上对象与表的配置文件路劲 -->
<mapping resource="com/rabbit/domain/Customer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
- 书写代码测试:注意导包不要导错
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public static void main(String[] args){
Configuration conf = new Configuration().configure();
SessionFactory sf = conf.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Customer c = new Customer();
c.setCust_name("百度公司");
session.save(c);//执行保存
tx.commit();
session.close();
sf.close();
}
配置文件详解
Orm元数局配置文件:
- 要去windows-perferences-XML Catalog里添加;Location:磁盘路径,key-type:URI;key:网址:http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- package 属性 填写一个包名,写了包名之后,需要填写完整类名的可以直接写简单类名 -->
<hibernate-mapping package="com.rabbit.domain">
<!--
Class元素:配置实体与表的对应关系
name:完整类名
table:数据库表名
-->
<class name="Customer" table="cst_customer">
<!-- id元素:配置主键映射的属性
name :填写主键对应的属性名
column:填写主键中的主键列名
-->
<id name="cust_id" column="cust_id">
<!-- generator:主键生成策略,就是每条记录录入时,主键的生成规则
identity:主键自增,由数据库来维护,录入时不需要指定主键
increment:(不推荐使用,increment存在线程安全问题,并发访问时会出现两个主键相同的情况)主键自增,由hibernate来维护,每次插入前会先查询表中id最大值,+1作为新主键值;
sequence:Oracle中的主键生成策略
hilo(不使用):高底位算法,主键自增,由hibernate来维护
native(推荐使用):hilo+sequence+identity;自动三个选一个策略
uuid:用来产生一个理论上永远不会重复的随机字符串作为主键
assigned:自然主键生成策略
-->
<generator class="native"></generator>
</id>
<!-- property配置除id之外的普通属性映射
name:对应属性名
column(可选):对应的表列名,默认值:默认使用name属性的值
type(可选,尽量别填):填写该列(属性)的类型,如果不填,hibernate会自动检测实体的属性
每个类型有三种填发:java hibernate sql
not-null:配置该属性是否不能为空,默认值是false;
length(可选):配置数据库该列的长度,默认值:会使用数据库类型的最大长度
-->
<property name="cust_name" column="cust_name"></property>
<property name="cust_source" column="cust_source"></property>
<property name="cust_industyr" column="cust_industyr"></property>
<property name="cust_level" column="cust_level"></property>
<property name="cust_linkman" column="cust_linkman"></property>
<property name="cust_phone" column="cust_phone"></property>
<property name="cust_mobile" column="cust_mobile"></property>
</class>
</hibernate-mapping>
书写主配置文件:文件名必须为:hibernate.cfg.xml:
- 要去windows-perferences-XML Catalog里添加;Location:磁盘路径,key-type:URI;key:网址:http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!--
#hibernate.dialect org.hibernate.dialect.MySQLDialect
#hibernate.dialect org.hibernate.dialect.MySQLInnoDBDialect
#hibernate.dialect org.hibernate.dialect.MySQLMyISAMDialect
#hibernate.connection.driver_class com.mysql.jdbc.Driver
#hibernate.connection.url jdbc:mysql:///test
#hibernate.connection.username gavin
#hibernate.connection.password
-->
<!-- 数据库驱动 -->
<property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<!-- 连接数据库url -->
<property name="hibernate.connection.url">jdbc:mysql:///mysqltest?serverTimezone=GMT%2B8</property>
<!-- 数据库用户名 -->
<property name="hibernate.connection.username">root</property>
<!-- 数据库密码 -->
<property name="hibernate.connection.password">admin</property>
<!-- 数据库方言
在不同的数据库中,sql语法略有区别,指定方言可以让hibernate在生存sql语句时,针对数据库方言实现
-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- #hibernate.show_sql true 打印sql语句
#hibernate.format_sql true 格式化sql语句
-->
<!-- 可选 -->
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<!--
## auto schema export 自动建表
#hibernate.hbm2ddl.auto create-drop 自动建表,每次框架运行结束会将所有表删除
#hibernate.hbm2ddl.auto create 自动建表,如果数据库有这个表就直接覆盖以前的表
#hibernate.hbm2ddl.auto update (推荐使用)自动生成表,如果已经存在,就不会再生成,如果表有变动会自动更新表,不会删除任何数据
#hibernate.hbm2ddl.auto validate 校验,任何情况都不会自动生成表,每次启动会校验数据库中表是否正确。校验失败就会抛出异常
-->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 引入orm元数据:
路径书写,填写src下的路劲;
-->
<mapping resource="com/rabbit/domain/Customer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
hibernate Api详解
Configuration:配置加载类,用于加载主配置和orm元数据加载
-
创建:调用空参构造即可
Configuration conf = new Configuration();
-
指定读取主配置文件;只要配置文件名叫hibernate.cfg.xml,直接空参加载方法(推荐使用)
Configuration conf = new Configuration().configure();
SessionFactory:创建session工厂:用于创建操作数据库核心对象Session对象的工厂;SessionFactory负责保存和使用所有的配置文件,所有它消耗内存资源非常大;SessionFactory属于线程安全的对象设计
-
保证在web项目中,只创建一个SessionFactory;
-
创建:通过加载完配置的Configuration的buildSessionFactory()方法生成SessionFactory;
SessionFactory sf = conf.buildSessionFactory();
-
获取Session对象:
-
通过SessionFactory的openSession()方法获得一个新的session对象;通过getCurrentSession()方法获得一个与线程绑定的同一个session对象
-
获得Session
Session session = sf.openSession();
Session:功能: 表达hibernate框架与数据库之间的连接(会话),类似于JDBC的Connection对象;还可以完成对数据库的增删改查操作;是hibernate操作数据的核心对象
-
session获得操作事务的Transaction对象
session.getTransaction();
-
session获得操作事务的Transaction对象并开启
Transaction tx = session.beginTransaction();
-
增加数据:
Customer c = new Customer(); c.setCust_name("阿萨德公司"); session.save(c);//执行保存
-
查询数据:根据id查询
Customer customer = session.get(Customer.class, 1L);
-
修改数据:修改id为1的cust_name为Rabbit
Customer customer = session.get(Customer.class, 1L); customer.setCust_name("Rabbit"); session.update(customer);
-
删除数据:
Customer customer = session.get(Customer.class, 1L); session.delete(customer);
Transaction:事务操作
-
提交事务
tx.commit();
-
回滚事务
tx.rollback();
向数据库增加数据完整代码:
package com.rabbit.domain;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class Demo1 {
public static void main(String[] args){
Configuration conf = new Configuration().configure();
SessionFactory sf = conf.buildSessionFactory();
Session session = sf.openSession();
//sf.getCurrentSession();
Transaction tx = session.beginTransaction();
//查询
//Customer customer = session.get(Customer.class, 1L);//查询id为1的数据
//修改:修改id为1的cust_name为
Customer customer = session.get(Customer.class, 1L);
customer.setCust_name("Rabbit");
session.update(customer);
//增加
Customer c = new Customer();
c.setCust_name("阿萨德公司");
session.save(c);//执行保存
tx.commit();
session.close();
sf.close();
}
}
hibernate工具类:单例。没有实例化,只能类名.调用
package com.rabbit.Utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
final class HibernateUtils {
private static SessionFactory sf ;
static {
Configuration conf = new Configuration().configure();
sf=conf.buildSessionFactory();
}
private HibernateUtils() {
}
public static Session openSession() {
Session session = sf.openSession();
return session;
}
public static Session getCurrentSession() {
Session session = sf.getCurrentSession();
return session;
}
}
Hibernate中实体规则
实体类创建的注意事项:
- 持久化类提供无参数构造
- 需提供属性:成员变量私有,提供共有get/set方法访问;
- 持久化类中的属性,尽量提供包装类型;
- 持久化类需要提供oid,与数据库中的主键列对应;(hibernate管理的数据库表必须要有主键)
- 不要用final 修饰class;
- hibernate使用cglib代理对象,代理对象是继承被代理对象,如果被final修饰,将无法继承,就无法代理;
主键类型
- 自然主键(不常用)
- 表的业务列中,有某业务列符合,必须有,并且不重复的特征时,该列可以作为主键使;
- 代理主键(常用)
- 表的业务列中,没有某业务列符合,必须有,并且不重复的热衷时,创建一个没有意义的列作为主键:id;
hibernate中的对象状态
- 对象分为三种状态:
- 瞬时状态:没有id,没有与session关联
- 持久化状态:有id,与session有关联
- 持久化状态的任何对象,都会自动同步到数据库;
- 游离/托管状态:有id,没有与session关联
hibernate进阶:一级缓存
- 缓存:提高效率;hibernate中的一级缓存也是为了提高操作数据库的效率;
- 提高效率1:提高查询效率
- 提高效率2:减少不必要的操作,比如:从数据库获取id为1l的对象会获取两个相同的对象,一个放入缓存,一个放入快照,如果最终提交时缓存的对象跟快照的对象相等,就不执行没必要的sql语句;
hibernate中的事务
-
事务特性:
- a原子性
- c一致性
- i隔离性
- d持久性
-
事务并发问题
- 1.脏读
- 2.不可重复读
- 3.幻|虚读
-
事务隔离级别
- 读未提交–123
- 读已提交–23
- 可重复读–3::(Mysql)
- 串行化 —没有问题,效率低
如何在hibernate中指定隔离级别:在主配置文件中
<!-- 指定hibernate操作数据库时的隔离级别
1 读未提交
2 读已提交
4 可重复读
8 串行化
-->
<property name="hibernate.connection.isolation">4</property>
在hibernate中确保使用同一个session对象的问题,hibernate已经帮我们解决了,我们只需要调用SessionFactory的getCurrentSeesion()方法即可获得与线程绑定的session对象;
- 注意: 调用getCurrentSession()方法必须在主配置文件中配置
<!-- 指定session与当前线程绑定 -->
<property name="hibernate.current_session_context_class">thread</property>
- 注意:通过getCurrentSession()方法获得的session对象,当事务提交时,session会自动关闭,不要手动调用close关闭资源;
hibernate中的批量查询
HQL查询:多表查询但不复杂时使用
- hql语句:全部查询:“from Customer”;条件查询:" from Customer where cust_id=?";
- 条件查询还可以使用命名占位符" from Customer where cust_id = :cust_id" 用query.setParameter(“cust_id”,1l);
- 代码演示:
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//书写hql语句:from domain where domain成员变量
String hql = " from Customer where cust_id=?";
//根据hql语句创建查询对象
Query query = session.createQuery(hql);
//设置参数:第一个?位置是0;jdbc是从1开始
query.setParameter(0, 1l);
//获取查询结果,直接可以返回domain对象,如果是查询多个返回一个List<Customer>;
Customer c = (Customer) query.uniqueResult();
System.out.println(c);
tx.commit();
session.close();
- 分页查询:
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//书写hql语句:from domain where domain成员变量
String hql = " from Customer";
Query query = session.createQuery(hql);
//设置分页信息:从第一页查,查询1条
query.setFirstResult(0);//类似于limit方法的第一个问号,从哪儿开始查询
query.setMaxResults(1);//类似于limit的第二个问号,查多少条语句
List<Customer> c = query.list();
System.out.println(c);
tx.commit();
session.close();
Criteria查询:无语句面向对象查询(常用于单表查询)
-
基本语法
@Test //Criteria查询语法 public void fun1() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //书写hql语句:如果没有第二个Customer,直接可以写" from Customer" //----------执行操作-------------------- //查询所有Customer对象 Criteria criteria = session.createCriteria(Customer.class); List<Customer> list = criteria.list(); System.out.println(list); //-------------------------------------- tx.commit(); session.close(); }
* 条件查询
@Test
//Criteria条件查询语法
/*
* > gt
* >= ge
* < lt
* <= le
* == eq
* != ne
* in in
* between and bettween
* like like
* is not null isNotNull
* is null isNull
* or or
* and and
*/
public void fun2() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//书写hql语句:如果没有第二个Customer,直接可以写" from Customer"
//----------执行操作--------------------
//得到criteria
Criteria criteria = session.createCriteria(Customer.class);
//查询id为1的Customer对象
criteria.add(Restrictions.eq("cust_id", 1l));
Customer c = (Customer) criteria.uniqueResult();
System.out.println(c);
//--------------------------------------
tx.commit();
session.close();
}
- 分页查询
@Test
//Criteria分页查询语法
public void fun3() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//书写hql语句:如果没有第二个Customer,直接可以写" from Customer"
//----------执行操作--------------------
//得到criteria
Criteria criteria = session.createCriteria(Customer.class);
//设置分页信息
criteria.setFirstResult(0);
criteria.setMaxResults(1);
List<Customer> list = criteria.list();
System.out.println(list);
//--------------------------------------
tx.commit();
session.close();
}
* Criteria查询总记录数语法;
@Test
//Criteria查询总记录数语法
public void fun4() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//书写hql语句:如果没有第二个Customer,直接可以写" from Customer"
//----------执行操作--------------------
//得到criteria
Criteria criteria = session.createCriteria(Customer.class);
//这只查询的聚合函数===>总行数
criteria.setProjection(Projections.rowCount());
Long i = (Long) criteria.uniqueResult();
System.out.println(i);
//--------------------------------------
tx.commit();
session.close();
}
原生Sql查询:就是以前的sql语句用法(复杂的业务查询)
* 基本查询:
@Test
//Sql查询
public void fun5() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//书写hql语句:如果没有第二个Customer,直接可以写" from Customer"
//----------执行操作--------------------
String sql = "select * from cst_customer";
//穿件查询对象
SQLQuery query =session.createSQLQuery(sql);
query.addEntity(Customer.class);//把查询的结果封装成Customer对象
List<Customer> list = query.list();
System.out.println(list);
/*
List<Object [] >list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
*/
//--------------------------------------
tx.commit();
session.close();
}
* 条件查询:
@Test
//Sql条件查询
public void fun5() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//书写hql语句:如果没有第二个Customer,直接可以写" from Customer"
//----------执行操作--------------------
String sql = "select * from cst_customer where cust_id=?";
//穿件查询对象
SQLQuery query =session.createSQLQuery(sql);
query.setParameter(0, 1l);
query.addEntity(Customer.class);//把查询的结果封装成Customer对象
List<Customer> list = query.list();
System.out.println(list);
/*
List<Object [] >list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
*/
//--------------------------------------
tx.commit();
session.close();
}
- 分页查询:跟以前一样
hibernate::1对多|多对1
- 用客户表跟联系人表来举例
- 关联表在联系人中使用一个外键cid列来关联
- 客户实体:private Set<联系人> linkMens;使用set集合在客户表中存储客户联系人;因为set集合可以排序,也可以去重复;
- 联系人实体:private 客户 Customer;
orm配置文件:
- 1对多:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- package 属性 填写一个包名,写了包名之后,需要填写完整类名的可以直接写简单类名 -->
<hibernate-mapping package="com.rabbit.domain">
<class name="Customer" table="cst_customer">
<id name="cust_id" column="cust_id">
<generator class="native"></generator>
</id>
<property name="cust_name" ></property>
<property name="cust_source"></property>
<property name="cust_industyr"></property>
<property name="cust_level" ></property>
<property name="cust_linkman" ></property>
<property name="cust_phone" ></property>
<property name="cust_mobile" ></property>
<!-- 集合,一对多关系,在配置文件中配置 -->
<!-- 集合属性名 -->
<set name="linkmens">
<!-- 外键列名 -->
<key column="lkm_cust_id"></key>
<!-- 与我关联的完整类名(因为上面已经指定了包位置,所以这里可以写简单类名) -->
<one-to-many class="LinkMan"/>
</set>
</class>
</hibernate-mapping>
- 多对1:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- package 属性 填写一个包名,写了包名之后,需要填写完整类名的可以直接写简单类名 -->
<hibernate-mapping package="com.rabbit.domain">
<class name="LinkMan" table="cst_linkman">
<id name="lkm_id">
<generator class="native"></generator>
</id>
<property name="lkm_gender"></property>
<property name="lkm_name"></property>
<property name="lkm_phone" ></property>
<property name="lkm_email" ></property>
<property name="lkm_qq" ></property>
<property name="lkm_mobile" ></property>
<property name="lkm_memo"></property>
<property name="lkm_position"></property>
<!-- 多对1 column属性值必须完全一样 -->
<!-- name :属性名
column: 外键列名
class:与我关联的对象完整类名
-->
<many-to-one name="customer" column="lkm_cust_id" class="Customer"></many-to-one>
</class>
</hibernate-mapping>
1对多,多对1关系操作:
- 添加客户并给它联系人
public void fun1() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer c = new Customer();
c.setCust_name("Rabbit");
LinkMan lm1 = new LinkMan();
lm1.setLkm_name("廖凡");
LinkMan lm2 = new LinkMan();
lm2.setLkm_name("lwf");
//1对多关系,客户下有多个联系人
c.getLinkmens().add(lm1);
c.getLinkmens().add(lm2);
//表达多对1,联系人属于哪个客户
lm1.setCustomer(c);
lm2.setCustomer(c);
session.save(c);
session.save(lm1);
session.save(lm2);
tx.commit();
session.close();
}
- 给已有客户添加联系人
@Test
//为客户添加联系人
public void fun2() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//操作----------------------------
//获得要操作的客户对象
Customer c = session.get(Customer.class, 5l);
//创建联系人
LinkMan lm1 = new LinkMan();
lm1.setLkm_name("李欣");
c.getLinkmens().add(lm1);
lm1.setCustomer(c);
session.save(lm1);
//------------------------------
tx.commit();
session.close();
}
- 给已有客户删除联系人
@Test
//为客户删除联系人
public void fun3() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
//操作----------------------------
//获得要操作的客户对象
LinkMan lm = session.get(LinkMan.class, 2l);
Customer c = session.get(Customer.class, 5l);
//删除
c.getLinkmens().remove(lm);
lm.setCustomer(null);
//------------------------------
tx.commit();
session.close();
}
1对多,多对1关系操作:进阶—级联操作
- 在Customer的orm配置文件下的set标签添加cascade标签使用级联操作:一般设置为save-update
<!-- 级联操作cascade
save-update:级联保存更新
delete:级联删除
all:save-update+delete
-->
<set name="linkmens" cascade="save-update">
<!-- 外键列名 -->
<key column="lkm_cust_id"></key>
<!-- 与我关联的完整类名(因为上面已经指定了包位置,所以这里可以写简单类名) -->
<one-to-many class="LinkMan"/>
</set>
-
配置之后代码演示:简化操作,只要保存c对象,自动将lm对象保存。简化操作
public void fun1() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer c = new Customer(); c.setCust_name("Rabbit"); LinkMan lm1 = new LinkMan(); lm1.setLkm_name("廖凡"); LinkMan lm2 = new LinkMan(); lm2.setLkm_name("lwf"); //1对多关系,客户下有多个联系人 c.getLinkmens().add(lm1); c.getLinkmens().add(lm2); //表达多对1,联系人属于哪个客户 lm1.setCustomer(c); lm2.setCustomer(c); session.save(c); tx.commit(); session.close(); }
1对多,多对1关系操作:进阶—关系维护属性:保存时,两个表都会维护;冗余了多余的维护关系语句;功能:减少hibernate执行的语句,提高效率
-
配置Customer的orm文件下的 set标签中的 inverse属性
<!-- inverse属性:配置关系是否维护 true:customer:不维护关系 false(默认值):customer维护关系 --> <set name="linkmens" cascade="save-update" inverse="true"> <!-- 外键列名 --> <key column="lkm_cust_id"></key> <!-- 与我关联的完整类名(因为上面已经指定了包位置,所以这里可以写简单类名) --> <one-to-many class="LinkMan"/> </set>
-
注意1:多的一方不能放弃维护(就是外键字段所在方);只有一的一方可以放弃维护;
-
注意1 :这种状态下不能删除一的一方,因为它不维护关系,但被多的一方引用,需要先将多的一方的外键引用设置为null;或者将cascade设置为"delete";
多对多
-
用员工表跟角色表来举例:外键用一个新表来充当:至少两个字段
-
员工orm配置文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- package 属性 填写一个包名,写了包名之后,需要填写完整类名的可以直接写简单类名 --> <hibernate-mapping package="com.rabbit.domain"> <class name="User" table="sys_user"> <id name="user_id" column="user_id"> <generator class="native"></generator> </id> <property name="user_code" ></property> <property name="user_name"></property> <property name="user_password"></property> <property name="user_state"></property> <!-- 多对多关系表达 name:集合属性名 table:配置中间表名 key:column:外键,别人引用我的外键名 class : 我与哪个类是多对多关系 column:外键,我引用别人的外键列名 --> <set name="roles" table="sys_user_role" cascade="save-update" inverse="true"> <!-- 外键列名 --> <key column="user_id"></key> <!-- 与我关联的完整类名(因为上面已经指定了包位置,所以这里可以写简单类名) --> <many-to-many class="Role" column="role_id"/> </set> </class> </hibernate-mapping>
-
角色orm配置文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- package 属性 填写一个包名,写了包名之后,需要填写完整类名的可以直接写简单类名 --> <hibernate-mapping package="com.rabbit.domain"> <class name="Role" table="sys_role"> <id name="role_id" column="role_id"> <generator class="native"></generator> </id> <property name="role_name" ></property> <property name="role_melo"></property> <!-- 集合,一对多关系,在配置文件中配置 --> <!-- 集合属性名 name--> <!-- 级联操作cascade save-update:级联保存更新 delete:级联删除 all :delete+save-update --> <!-- inverse属性:配置关系是否维护 true:customer:不维护关系 false(默认值):customer维护关系 --> <set name="users" table="sys_user_role" cascade="save-update" inverse="true"> <!-- 外键列名 --> <key column="role_di"></key> <!-- 与我关联的完整类名(因为上面已经指定了包位置,所以这里可以写简单类名) --> <many-to-many class="User" column="user_id"/> </set> </class> </hibernate-mapping>
测试:向数据库保存数据:
-
注意:由于多对多需要用一张中间表作为外键,所以两方必须有一方放弃维护关系,只由一方维护关系。也就是一方设置inverse=“true”,另外一方设置为false;
Test //保存员工以及角色 public void fun1() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); ///操作-------------------------- //创建两个User User u1 = new User(); u1.setUser_name("杨超越"); User u2 = new User(); u2.setUser_name("肖仰希"); //创建两个Role Role r1 = new Role(); r1.setRole_name("保安"); Role r2 = new Role(); r2.setRole_name("保洁"); u1.getRoles().add(r1); u1.getRoles().add(r2); u2.getRoles().add(r1); u2.getRoles().add(r2); r1.getUsers().add(u1); r1.getUsers().add(u2); r2.getUsers().add(u1); r2.getUsers().add(u2); session.save(u1); session.save(u2); session.save(r1); session.save(r2); //-------------------- tx.commit(); session.close(); }
查询HQL语法
//HQl语法详解
public class Demo3 {
@Test
//基本语法
public void fun1() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
String hql1 = "from com.rabbit.Custuomer";//完整语法
String hql = "from Customer";//简单语法
Query query = session.createQuery(hql);
List <Customer> list = query.list();
System.out.println(list);
tx.commit();
session.close();
}
@Test
//排序语法
public void fun2() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
String hql1 = "from com.rabbit.domain.Customer order by cust_id asc";//完整语法
String hql = "from Customer order by cust_id desc";
Query query = session.createQuery(hql);
List <Customer> list = query.list();
System.out.println(list);
tx.commit();
session.close();
}
@Test
//条件查询语法
public void fun3() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
String hql = "from Customer where cust_id=?";
Query query = session.createQuery(hql);
query.setParameter(0, 3l);
Customer c = (Customer) query.uniqueResult();
System.out.println(c);
tx.commit();
session.close();
}
@Test
//分页查询语法
public void fun4() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
String hql = "from Customer ";
Query query = session.createQuery(hql);
query.setFirstResult(0);//第0条开始
query.setMaxResults(2);//取两条
List<Customer> list = query.list();
System.out.println(list);
tx.commit();
session.close();
}
@Test
//统计查询语法
public void fun5() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// String hql = "select count(*) from Customer ";
// String hql = "select cust_name from Customer ";
String hql = "select new Customer(cust_id,cust_name) from Customer ";
Query query = session.createQuery(hql);
// Number n = (Number) query.uniqueResult();
List<Customer> list = query.list();
System.out.println(list);
tx.commit();
session.close();
}
//----------------------------------------------------------------------------------
@Test
//多表查询语法
/*
* 内连接(迫切):
*
* 外链接:
* 左外(迫切)
* 右外(迫切)
*/
public void fun6() {
//内连接
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
String hql = "from Customer c inner join c.linkmens";
String hql1 = "from Customer c inner join fetch c.linkmens";//迫切
Query query = session.createQuery(hql1);
List<Customer> list = query.list();
System.out.println(list);
tx.commit();
session.close();
}
@Test
public void fun7() {
//左右外链接
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
String hql = "from Customer c left join c.linkmens";//左外
String hql1 = "from Customer c right join c.linkmens";//右外
Query query = session.createQuery(hql);
List<Object []> list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
tx.commit();
session.close();
}
查询Criteria语法
基本查询:
//criteria语法
public class Demo4 {
@Test
public void fun1() {
//基本语法
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Criteria c = session.createCriteria(Customer.class);
List<Customer> list = c.list();
System.out.println(list);
tx.commit();
session.close();
}
@Test
public void fun2() {
//条件语法
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Criteria c = session.createCriteria(Customer.class);
c.add(Restrictions.idEq(3l)); //id为3的数据
List<Customer> list = c.list();
System.out.println(list);
tx.commit();
session.close();
}
@Test
public void fun3() {
//分页语法
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Criteria c = session.createCriteria(Customer.class);
c.setFirstResult(0);
c.setMaxResults(2);
List<Customer> list = c.list();
System.out.println(list);
tx.commit();
session.close();
}
@Test
public void fun4() {
//排序语法
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Criteria c = session.createCriteria(Customer.class);
c.addOrder(Order.desc("cust_id"));
List<Customer> list = c.list();
System.out.println(list);
tx.commit();
session.close();
}
@Test
public void fun5() {
//统计语法
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Criteria c = session.createCriteria(Customer.class);
c.setProjection(Projections.rowCount());//==select count(*) from cst_customer
Number n = (Number) c.uniqueResult();
System.out.println(n);
tx.commit();
session.close();
}
离线查询Criteria:
-
基本Criteria是通过session创建
-
离线查询可以直接凭空创建Criteria对象;组装查询条件;
-
作用:可以使用一个Dao层方法实现多种查询方式;只需要在web层创建Criteria,组装查询条件传过去就行;
//--------离线Criteria查询-------------- @Test public void fun6() { DetachedCriteria dc = DetachedCriteria.forClass(Customer.class); Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); dc.add(Restrictions.idEq(3l)); // Criteria c = dc.getExecutableCriteria(session); List list = c.list(); System.out.println(list); tx.commit(); session.close(); }
查询优化
类级别查询:
-
懒加载/延迟加载(建议使用):
//懒加载/延迟加载 //是否延迟加载:可以通过在配置文件class元素上配置lazy属性来控制:lazy="true"加载时不查询 public class Demo5 { @Test public void fun1() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //load()方法实在执行时不发送任何sql语句,会先返回一个对象,在使用该对象时,才执行查询 Customer c = session.load(Customer.class, 3l); System.out.println(c); tx.commit(); session.close(); }
关联级别查询:为了提高效率,fetch的选择应选select。lazy的选择应选择true;全使用默认值
orm配置文件Set标签里的两个属性:集合关联Cusomer.hbm.xml
lazy:决定是否延迟加载
true;延迟加载
false;立即加载
extra:极其懒惰;----与懒加载基本一致,只有在查询size的时候会只查询count;
fetch:决定加载策略,使用什么类型的sql语句加载集合数据
select(默认值); -----------单表查询
join:使用多表查询加载集合------多表查询,当fetch="join",lazy属性全部无效,全部立即加载
subselect:使用子查询加载集合
对象策略加载;策略关联:LinkMan.hbm.xml
fetch: 决定加载的sql语句
select :使用单表查询
join:多表查询; 当为join时,lazy直接失效
lazy:决定加载时机
false:立即加载
proxy(默认值):由Customer的lazy决定,如果Customer的为true
no-session问题解决:扩大session的作用范围,设置Filter,在放行前打开session,放行后关闭session