Hibernate框架
Hibernate框架
1 hibernate是什么?
hibernate是一个框架。
hibernate是一个持久化框架。
hibernate是一个封装了JDBC的持久化框架。
以后使用hibernate不用写jdbc了,select ,insert , delete , update
Hibernate是一个基于ORM的封装JDBC的持久化框架。
2 持久化?
化--过程。
程序中的数据是临时。
将程序中临时性的数据持久的保存起来的过程。叫持久化。
1数据库---jdbc
2文件操作---io
3 ORM?
对象关系映射。
对象:java程序中类
关系:关系型数据库中的表
O;
public class Goods implements java.io.Serializable { // Fields private Integer goodsId; private String goodsName; private Double goodsPrice; private Integer goodsNum; |
R:
CREATE TABLE goods ( goods_id int(11) NOT NULL auto_increment, goods_name varchar(200) default NULL, goods_price double default NULL, goods_num int(11) default NULL, PRIMARY KEY ('goods_id') ) |
M:Goods.hbm.xml文件,ORM文件
<hibernate-mapping> <class name="com.no8.domain.Goods" table="goods" catalog="hibernate"> <id name="goodsId" type="java.lang.Integer"> <column name="goods_id" /> <generator class="identity"></generator> </id> <property name="goodsName" type="java.lang.String"> <column name="goods_name" length="200" /> </property> <property name="goodsPrice" type="java.lang.Double"> <column name="goods_price" precision="22" scale="0" /> </property> <property name="goodsNum" type="java.lang.Integer"> <column name="goods_num" /> </property> </class> </hibernate-mapping> |
4 手动实现Hibernate的第一个案例(基于hibernate4.3.5版本)
4.1 创建工程并导入hibernate的jar文件
4.2 编写实体类和数据库关系表
public class Goods implements java.io.Serializable { // Fields private Integer goodsId; private String goodsName; private Double goodsPrice; private Integer goodsNum; |
CREATE TABLE goods ( goods_id int(11) NOT NULL auto_increment, goods_name varchar(200) default NULL, goods_price double default NULL, goods_num int(11) default NULL, PRIMARY KEY ('goods_id') ) |
4.3 编写ORM文件
<?xml version="1.0"?> <!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.no8.domain.Goods" table="goods"> <id name="goodsId" column="goods_id"> <generator class="identity" /> </id> <property name="goodsName" type="java.lang.String" column="goods_name" /> <property name="goodsPrice" type="java.lang.Double" column="goods_price" /> <property name="goodsNum" type="java.lang.Integer" column="goods_num" /> </class> </hibernate-mapping> |
4.4 编写Hibernate的配置文件-hibernate.cfg.xml
在工程的src目录下创建一个名称为hibernate.cfg.xml的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> <!-- Database connection settings --> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://127.0.0.1:3306/no8?useUnicode=true&characterEncoding=utf8</property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- 方言 --> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <!-- 显示Hibernian生成的SQL语句 --> <property name="show_sql">true</property> <!-- 加载ORM映射文件 --> <mapping resource="com/no8/domain/Goods.hbm.xml"/> </session-factory> </hibernate-configuration> |
4.5 编写工具类加载Hibernate配置文件创建sessionFactory
基于hibernate4版本。在hiberante3版本中不要修改
Configuration configuration = new Configuration().configure(); ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() .applySettings(configuration.getProperties()).build(); SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry); |
4.6 从SessionFactory中创建Session对象
Session session = HibernateUtil.getSessionFactory().openSession(); |
4.7 通过调用session对象的方法实现数据库的CURD操作
public class SaveTest { public static void main(String[] args) { Goods goods = new Goods(); goods.setGoodsName("索爱"); goods.setGoodsNum(1); goods.setGoodsPrice(35D); Session session = HibernateUtil.getSessionFactory().openSession(); Transaction tran = session.getTransaction(); tran.begin(); session.save(goods); tran.commit(); session.close(); HibernateUtil.getSessionFactory().close(); System.out.println("end"); } } |
public class FindById { public static void main(String[] args) { Session session = HibernateUtil.getSessionFactory().openSession(); Goods goods = (Goods) session.get(Goods.class, 3); session.close(); System.out.println(goods.getGoodsName()); System.out.println(goods.getGoodsPrice()); System.out.println("end"); } } |
public class Update { public static void main(String[] args) { Session session = HibernateUtil.getSessionFactory().openSession(); Goods goods = (Goods) session.get(Goods.class, 3); goods.setGoodsNum(100); Transaction tran = session.getTransaction(); tran.begin(); session.update(goods); tran.commit(); session.close(); System.out.println("end"); } } |
public class Delete { public static void main(String[] args) { Session session = HibernateUtil.getSessionFactory().openSession(); Goods goods = (Goods) session.get(Goods.class, 3); Transaction tran = session.getTransaction(); tran.begin(); session.delete(goods); tran.commit(); session.close(); System.out.println("end"); } } |
public class FindAll { public static void main(String[] args) { String hql ="from com.no8.domain.Goods"; Session session = HibernateUtil.getSessionFactory().openSession(); Query query = session.createQuery(hql); List<Goods> goodsList = query.list(); for (Goods goods : goodsList) { System.out.println(goods.getGoodsName()+":"+goods.getGoodsPrice()); } session.close();
System.out.println("end"); } } |
5 Hibernate中核心类和接口
5.1 Session接口
hibernate框架最核心的接口,所有操作数据库的方法都是这个对象的方法。
session.save() session.update() session.delete() session.get() session.load()
5.2 SessionFactory工厂类
hibernate框架中的session工厂,负责生成session对象。
生产的session对象由Hibernate.cfg.xml文件中配置信息决定的。
sessionFactory.openSession();
5.3 Configuration类
负责读取Hibernate.cfg.xml文件的类
5.4 Transaction事务对象
进行数据库增删改操作时要使用事务,就是hibernate中的事务对象
Transactiontran = session.getTransaction();
tran.begin() tran.commit() tran.rollback()
5.5 Query接口
Query接口是一个在hibernate中执行查询语句hql语句的一个接口。
Queryquery = session.createQuery("hql语句");
6 使用MyEclipse工具创建第一个Hibernate案例
6.1 创建工程
6.2 在MyEclipse中创建一个数据库的连接
6.3 为工程加入hibernate框架
针对MySql数据库中文乱码的配置
<propertyname="connection.useUnicode">true</property>
<propertyname="connection.characterEncoding">UTF-8</property>
6.4 使用工具反向生成实体类和ORM文件
换到database面板-->数据表名上右键-->Hibernate....
6.5 编写Service
public class GoodsService { private GoodsDAO goodsDAO = new GoodsDAO(); public boolean save(Goods goods) { Transaction tran = HibernateSessionFactory.getSession().getTransaction(); try { tran.begin(); this.goodsDAO.save(goods); tran.commit(); return true; } catch (Exception e) { e.printStackTrace(); tran.rollback(); return false; } } } |
7 Hibernate的配置文件-hibernate.cfg.xml
7.1 使用连接池C3P0
1导入C3P0连接池的jar文件
手动编写工程时使用jar位置
使用工具自动方式
2在hibernate.cfg.xml文件中配置c3p0的相关设置
7.2 show_sql和format_sql属性
show_sql= true|false 表示显示Hibernate生成的sql语句
format_sql= true|false 表示是否格式化显示的sql语句
8 使用log4j日志显示更详细的信息
project-etc-log4j.properties
log4j.logger.org.hibernate.type=trace
9 ORM文件
9.1 动态插入和动态更新
在ORM文件中的<class节点上的二个属性
<class dynamic-insert="false" dynamic-update="false"> |
dynamic-insert="true|false"动态插入,true表示启动动态插入
dynamic-update="true|false"动态更新
动态插入:dynamic-insert="true"
当对象的属性值为null时,将不会在生成的sql语句中出现对应字段的插入。
数据库表结构:加入了一个带有系统当前时间做为默认值的日期时间字段
goods_date字段有默认值,在程序中用不用赋值?不用。在程序中默认值为null
当我们保存Goods时,没有设置动态插入:dynamic-insert="false"
所以在数据库的记录上goods_date字段的值为null.
当我们保存Goods时,有设置动态插入:dynamic-insert="true"
在生成的sql语句中没有goods_date字段,
所以在数据库表中的记录上goods_date字段的值用数据库的默认值。
动态更新:dynamic-update="true"
当对象的属性没有发生变化时,在Hibernate生成的update语句中不会出现相对应的字段的修改。
update表名 set 字段=值,字段=值 where 条件;
dynamic-update="true"
public static void main(String[] args) { // TODO Auto-generated method stub GoodsService goodsService = new GoodsService();
Goods goods = goodsService.findById(15);
goods.setGoodsPrice(goods.getGoodsPrice()*0.8);
goodsService.update(goods);
System.out.println("end"); } |
10 延迟加载 <classlazy="true|false"
加载在hibernate应用中表示从数据库中查询数据。
英:lazy 中:懒
lazy="true"时延迟加载
延迟加载 : 就是等一会再查询数据,不用不查询,当使用查询对象的非主键字段时才会向数据库发送查询语句。
10.1 延迟加载的条件
只有在同一个会话没有关闭的时候,延迟加载才能正常使用。
Session session = HibernateSessionFactory.getSession(); Goods goods = (Goods) session.load(Goods.class, 1); HibernateSessionFactory.closeSession();
System.out.println(goods.getGoodsId()); System.out.println(goods.getGoodsName()); System.out.println(goods.getGoodsPrice()); System.out.println("end"); |
第一次使用goods对象的属性是goods.getGoodsName(),但是在使用这个方法之前已经closeSession了。
如果会话已经关闭,再使用延迟加载则会发生以下的异常:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session |
11 延迟加载对WEB应用程序的影响
关闭在延迟加载之前,所以程序会在goods.jsp页面上报出异常,
LazyInitializationException: could not initialize proxy - no Session |
使用过滤器对响应进行过滤,在过滤时关闭session。
创建一个关闭会话的过滤器。OpenSessionInViewFilter.java
GoodsService
不能再在业务层关闭会话对象了。
public class GoodsService { private GoodsDAO goodsDAO = new GoodsDAO(); public Goods findById(Integer id) { return this.goodsDAO.findById(id); } public List<Goods> findAll() { return this.goodsDAO.findAll(); } } |
OpenSessionInViewFilter
public class OpenSessionInViewFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); HibernateSessionFactory.closeSession(); } |
<filter> <filter-name>opensession</filter-name> <filter-class>com.no8.filter.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>opensession</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> |
12主键生成策略generator
在每个实体类的映射中都有一个主键的映射
<class> 实体类映射
<id> 主键映射
<generatorclass="identity"></generator> 主键生成策略
</id>
</class>
11.1 increment自增长。按主键字段最大值+1的方式实现主键生成的策略
Hibernate: select max(goods_id) from goods Hibernate: insert into hibernate.goods (goods_name, goods_price, goods_num, goods_id) values (?, ?, ?, ?) 保存成功! |
11.2 identity标识列,针对mysql数据库。
11.3 native 自动,让Hibernate自己看着办
11.4 assigned 手动设置,由程序说了算。
11.5 sequence序列,针对Oracle数据库。
oracle数据库中的表
oracle数据库中的序列对象
ORM映射文件:
在主键映射信息中的主键生成策略:
<generator class="sequence"></generator> 只能表示使用序列,但用哪个序列对象没有,默认hibernate_sequence
在oracle数据库中有一个叫"MYSEQ"的序列。指明使用这个序列对象
<generator class="sequence"> <param name="sequence">MYSEQ</param> </generator> |
生成的sql语句
Hibernate: select MYSEQ.nextval from dual Hibernate: insert into NO8.GOODS (GOODS_NAME, GOODS_PRICE, GOODS_NUM, GOODS_TYPE, GOODS_ID) values (?, ?, ?, ?, ?) end |
13 对比SessionFactory中两种获得Session的方式
13.1 在使用SessionFactory获得session的方式有两种
SessionFactory sf = HibernateSessionFactory.getSessionFactory(); //第一种从sf中获得session的方式 Session session1 = sf.openSession(); //第二种从sf中获得session的方式 Session session2 = sf.getCurrentSession(); |
13.2 openSession()
这是一个很单纯的方法,每调用一次就从sf中获得一个新的会话(Session)对象。
程序中对于openSession方法的使用需要配置合其他代码。才能事务的完整。
为了保存在Hibernate中事务的完整,必须使用同一个会话对象,所以使用openSession时要配合ThreadLocal<Session>
13.3 getCurrentSession()
这是一个获得当前线程中的会话对象的方法。
在Hibernate框架中已经对会话对象进行了管理,也会有本地线程对象保存会话对象,
getCurrentSession方法可以从当前线程中获得会话对象
所以为了事务的完整,在使用getCurrentSession方法时不用配合其他类的使用。
13.3.1 注意一:必须在Hibernate.cfg.xml中有相关设置
HibernateException: No CurrentSessionContext configured!
在Hibernate.cfg.xml文件中加入如下配置信息:
<property name="current_session_context_class">thread</property>
13.3.2 注意二:必须在事务环境下执行
HibernateException: get is not valid without active transaction
13.3.3 注意三:会话对象不能再手动关闭
使用getCurrentSession时,当事务进行提交或回滚操作时会自动关闭会话对象。
SessionException:Session was already closed
14 对于session的get和load方法
get方法和load方法都是按主键查询的方法
get方法表示获得一个对象。
load方法表示加载一个对象。
14.1 get方法
get方法表示获得一个对象,
get方法不会参考<class lazy的设置,都是立即加载类对象。
get方法返回的对象的类型就是对象本身的类型
14.2 load方法
load方法表示加载一个对象
load方法会参考<class lazy的设置,当lazy="true"时使用延迟加载 。
load方法返回的对象的类型是一个代理对象。代理了Goods对象原有的功能的同时附加了新的功能。
14.3 代理模式的第一个演示
public class ProxyGoods extends Goods { @Override public String getGoodsName() { System.out.println("查询数据库!"); return super.getGoodsName(); } } |
Goods g = new ProxyGoods(); g.setGoodsName("刘洁"); System.out.println(g.getGoodsName()); |
15 持久化对象的三种状态
持久化类:一个对后台数据库中表有映射关系的类。
Goods.java Goods.hbm.xml文件
Goods这个类就是持久化类。
持久化对象:持久化类的实例。
Goodsgoods = new Goods(); goods就是持久化对象。
一个持久化对象一共有三种状态:
1临时态transient
2 持久态persistent
3 脱管态detached
15.1 区分三种状态
id(是否有主键值) session(是否有打开的会话对对象进行管理)
临时态transient null 没有session
持久态persistent 非空,有记录 有session
脱管态detached 非空 没有session
一个保存商品的过程。
Goods goods = new Goods();//临时状态对象goods goods.setGoodsName("清风"); goods.setGoodsPrice(2D); goods.setGoodsNum(10);
Session session = HibernateSessionFactory.getSession();
Transaction tran = session.getTransaction(); tran.begin(); session.save(goods);//保存成功之后,持久状态的goods tran.commit(); HibernateSessionFactory.closeSession();
System.out.println(goods.getGoodsName());//session关闭之后,脱管状态的goods对象 |
15.2 持久状态对象对数据库的影响
持久状态对象特点,与数据库保持连接,同时数据库中有一条记录与对象对应。
持久状态对象的修改等直接修改数据库中对应的记录。
持久状态对象就是关联着一个记录,对持久状态对象属性的修改就是对记录的修改。
15.3 三种状态之间的变化规律
16 单表的HQL语句
进行数据库的查询,
1get
2load
3DAO.findall()-hql语句
16.1 hql是什么?
hql是hibernate查询语句,语法与sql基本一致
hql与sql最大的不同,hql语句是一个针对类进行查询语句。
hql语句是一种面向对象的查询语句。
16.2 Query接口
org.hibernate.Query
Query接口是在Hibernate中执行hql语句的接口。
当我们想使用hql语句进行查询时必须先获得Query接口的实例。
16.3 获得Query接口的实例
Queryquery = session.createQuery("hql语句");
16.4 使用Query接口的方法查询数据库记录
使用Query接口的方法进行数据库查询,面向对象的方式实现查询。
1list() 返回一个查询的集合
String hql = "from com.no8.domain.Goods"; Query query = HibernateSessionFactory.getSession().createQuery(hql); List<Goods> goodsList = query.list(); HibernateSessionFactory.closeSession();
for (Goods goods : goodsList) { System.out.println(goods.getGoodsId()+":"+goods.getGoodsName()); } |
2uniqueResult() 返回一个查询的对象
String hql ="from Goods where goodsId=95"; Query query = HibernateSessionFactory.getSession().createQuery(hql); Goods goods = (Goods) query.uniqueResult(); HibernateSessionFactory.closeSession(); System.out.println(goods.getGoodsId()); System.out.println(goods.getGoodsName()); |
16.5 在hql语句中使用参数-?
要hql语句中我们可以使用"?"的方式来增加参数。
在执行hql语句时,使用Query接口的方法为参数赋值。
String hql ="from Goods where goodsId=?"; Query query = HibernateSessionFactory.getSession().createQuery(hql); query.setInteger(0, 2); Goods goods = (Goods) query.uniqueResult(); HibernateSessionFactory.closeSession(); System.out.println(goods.getGoodsId()); System.out.println(goods.getGoodsName()); |
16.6 在hql语句中使用参数,使用参数名(推荐)
要hql语句中我们可以使用":参数名"的方式来增加参数。
在执行hql语句时,使用Query接口的方法为参数赋值。
String hql ="from Goods where goodsId=:goodsId"; Query query = HibernateSessionFactory.getSession().createQuery(hql); query.setInteger("goodsId", 4); Goods goods = (Goods) query.uniqueResult(); HibernateSessionFactory.closeSession(); System.out.println(goods.getGoodsId()); System.out.println(goods.getGoodsName()); |
16.7 使用query接口的setProperties(Object)方法为参数赋值
from Goods where goodsName like :goodsName and goodsPrice between :min and :max
几个参数?3,goodsName,min,max
创建一个保存查询条件值的实体类:
public class SearchVO { private String goodsName; private double min; private double max; |
在使用Query接口的setProperties(实体类的对象)
Hibernate会自动将SearchVO类的属性的值与HQL语句中的参数对应,名称一致。
16.8 使用Hibernate实现多条件查询基于查询对象SearchVO
16.8.1 创建查询对象SearchVO
public class SearchVO { private String goodsName; private Double min; private Double max; private Integer goodsNum; |
16.8.2 在DAO中编写一个多条件查询的方法find(SearchVO searchVO)
public List<Goods> find(SearchVO searchVO){ log.debug("finding all Goods instances"); try { String queryString = "from Goods where 1=1 "; if(searchVO.getGoodsName() !=null ){ queryString+= "and goodsName like :goodsName "; } if(searchVO.getMax()!=null){ queryString+= "and goodsPrice <= :max "; } Query queryObject = getSession().createQuery(queryString); queryObject.setProperties(searchVO); return queryObject.list(); } catch (RuntimeException re) { log.error("find all failed", re); throw re; } } |
16.9 使用query接口的setProperties(Map)方法为参数赋值
@Test public void query4() { String hql = "from Goods where goodsName like :goodsName and " + "goodsPrice between :min and :max"; Map<String, Object> map = new HashMap<String, Object>(); map.put("goodsName", "%"); map.put("min", 10D); map.put("max", 100D); Query query = HibernateSessionFactory.getSession().createQuery(hql); query.setProperties(map); List<Goods> goodsList = query.list(); HibernateSessionFactory.closeSession(); for (Goods goods : goodsList) { System.out.println(goods.getGoodsName() + ":" + goods.getGoodsPrice()); } System.out.println("end"); } |
16.10 Hql语句也支持分页查询
在Hibernate中分页的实现是通过调用Query接口的丙个方法实现的。
query.setFirstResult(5);//当前查询时启始的索引
query.setMaxResults(5);//查询的记录个数
query.list();
16.11 hql的投影查询-select
在hql语句中没有select *
当我们只想查询某几列时,称投影查询,就要使用select 属性名,属性名
16.11.1 select goodsName,goodsPrice from Goods
返回类型是一个List<Object[]>。
hibernate会将每条记录做成一个Object[],数组中的第一个元素就是一行的每个属性。
查询结构有多条记录时,就做集合将多个Object[]放到集合中。
List<Object[]>list = query.list();
16.11.2 可以利用类的创建方法修改返回类型
0投影查询的hql语句
select new Goods(goodsName,goodsPrice) from Goods |
1在Goods类上,创建一个有参构造
public Goods(String goodsName, Double goodsPrice) { super(); this.goodsName = goodsName; this.goodsPrice = goodsPrice; } |
2接收时Hibernate就能够使用构造方法自动实体化为Goods类的实例。
public void query7(){ String hql = "select new Goods(goodsName,goodsPrice) from Goods"; Query query = HibernateSessionFactory.getSession().createQuery(hql); List<Goods> goodsList = query.list(); HibernateSessionFactory.closeSession(); for (Goods goods : goodsList) { System.out.println(goods.getGoodsName() + ":" + goods.getGoodsPrice()); } } |
16.12 Hql支持函数的使用
sql聚合函数: sum , count,min , max ,avg
sql语句: select count(*) from 表名
hql语句: select count(*) from 类名
public void query8(){ String hql = "select count(*) from Goods"; Query query = HibernateSessionFactory.getSession().createQuery(hql); Long o = (Long)query.uniqueResult(); HibernateSessionFactory.closeSession(); System.out.println(o); } |
public void query9(){ String hql = "select sum(goodsPrice),min(goodsPrice),max(goodsPrice)" + " from Goods"; Query query = HibernateSessionFactory.getSession().createQuery(hql); Object[] objs = (Object[]) query.uniqueResult(); HibernateSessionFactory.closeSession(); for (Object object : objs) { System.out.println(object); } } |
在Hql中可以使用map来优化函数的使用。
public void query10(){ String hql = "select" + " new Map(sum(goodsPrice) as sum,min(goodsPrice) as min)" + " from Goods"; Query query = HibernateSessionFactory.getSession().createQuery(hql); Map<String,Double> map = (Map<String,Double>) query.uniqueResult(); HibernateSessionFactory.closeSession(); System.out.println(map.get("sum")); System.out.println(map.get("min")); // Map map = new HashMap(); // map.put("name", "peter"); // map.put("address", "北京"); // System.out.println(map); } |
public void query11(){ String hql = "select" + " new com.no8.vo.GoodsSum(sum(goodsPrice),min(goodsPrice))" + " from Goods"; Query query = HibernateSessionFactory.getSession().createQuery(hql); GoodsSum goodsSum = (GoodsSum) query.uniqueResult(); HibernateSessionFactory.closeSession(); System.out.println(goodsSum.getSum()); System.out.println(goodsSum.getMin()); } |
public class GoodsSum { private Double sum; private Double min; |
16.13 hql支持分组group by
public void query12() { String hql = "select count(*) from Goods group by goodsName"; Query query = HibernateSessionFactory.getSession().createQuery(hql); List<Long> ls = query.list(); HibernateSessionFactory.closeSession(); for (Long long1 : ls) { System.out.println(long1); }
|
@Test public void query13() { String hql = "select" + " new Map(count(*) as count , sum(goodsNum) as sum)" + " from Goods group by goodsName"; Query query = HibernateSessionFactory.getSession().createQuery(hql); List<Map> ls = query.list(); HibernateSessionFactory.closeSession(); for (Map map : ls) { System.out.println(map.get("count") + "=" + map.get("sum")); } } |
@Test public void query14() { String hql = "select" + " new Map(goodsName as name,count(*) as count , sum(goodsNum) as sum)" + " from Goods group by goodsName"; Query query = HibernateSessionFactory.getSession().createQuery(hql); List<Map> ls = query.list(); HibernateSessionFactory.closeSession(); for (Map map : ls) { System.out.println(map.get("name") + "=" + map.get("count") + "=" + map.get("sum")); } } |
17 关联关系映射-单向多对一映射(基础)
关联关系:在数据库表与表之间是有关系。在数据库两张表之间的关系维护的手段只有一个,“主外键约束”
在Hibernate中也要能维护在数据库中的表与表之间的关联关系。
17.1 数据库中表的关系模型
数据库中两张表:商品与类型
类型表:
商品表:
两张表在数据库中有没有关系?
有关系,在数据库使用外键的方式创建的关系。
在Goods表中有一个外键字段,引用Goods_type表。
在数据库中两张表有关系,产生关系字段,关系映射基数
一个商品可以属于一个类型
一个类型可以拥有多个商品
所以我们把 商品->类型 的关系称为 多对一
类型->商品 的关系称为 一对多
最终:Hibernate中关联关系映射-单向多对一映射,,表示我们只考虑从商品到类型的映射。
17.2 Hibernate的生成的实体类与ORM文件
17.2.1 先生成类型Goods_type表
GoodsType.java
public class GoodsType implements java.io.Serializable { // Fields private Integer typeId; private String typeName; private Integer typeLv; private String typePath; |
GoodsType.hbm.xml文件
17.2.2 生成有单向多对一映射关系的Goods表
Goods.java类
public class Goods implements java.io.Serializable { // Fields private Integer goodsId; private GoodsType goodsType;//这个就是Goods的关联关系对象 private String goodsName; private Float goodsPrice; private Integer goodsNum; private String goodsImg; private Timestamp goodsDate; |
private GoodsType goodsType;//这个就是Goods的关联关系对象
在关系型数据库中两张表之间有关系,用外键关联。
我们在商品表中保存一个类型的主键字段做为外键,保证两张表之间的关系。
商品表中的外键字段存在的意义?能找到goods_type表中的一条记录。
我们java程序是一个面向对象的语言,我们可以商品类中直接创建商品类型对象。
Goods.hbm.xml文件
在这个文件中说明Goods中是如何 从一个goods_type字段变成 了一个GoodsType的对象的。
<class name="com.no8.Goods" table="goods"> <many-to-one name="goodsType" class="com.no8.domain.GoodsType" fetch="select"> <column name="goods_type" /> </many-to-one> </class> |
17.3 可以级联操作Goods类的关联关系对象goodsType
GoodsDAO goodsDAO = new GoodsDAO(); Goods goods = goodsDAO.findById(1);
System.out.println(goods.getGoodsName()); System.out.println(goods.getGoodsImg()); System.out.println(goods.getGoodsType().getTypeName());
HibernateSessionFactory.closeSession(); |
17.4 <many-to-one fetch="select|join" 抓取策略
加载Goods时级联加载goodsType,怎么加载goodsType
fetch="select"表示使用一条单独的Select语句加载goodsType.
fetch="join"表示使用连接查询的方法加载goodsType
17.5 <many-to-one lazy="proxy|false" 延迟加载,关联级延迟加载
以延迟的方式加载Goods的级联关系对象goodsType
但是当goodsType已经加载完成之后 ,后面就直接引用之前加载完成了GoodsType对象。
一个数据库的记录只有一个持久化状态的对象。
17.6 基于MTO的CRUD
17.6.1 保存一个新商品
基于MTO,保存一个新商品,有关联关系对象GoodsType。
1保存一个新商品,属于现在有一个商品类型
//1 保存一个新商品,属于现在有一个商品类型(3 服装服饰) Goods goods = new Goods(); goods.setGoodsDate(DateUtil.getTimeStamp()); goods.setGoodsImg("wsc.jpg"); goods.setGoodsName("包(粉红版)"); goods.setGoodsPrice(100F); goods.setGoodsNum(1); //商品的级联关系对象商品类型,如果是数据库中存在的,应该从数据库中加载 GoodsTypeDAO goodsTypeDAO = new GoodsTypeDAO(); GoodsType goodsType = goodsTypeDAO.findById(3); goods.setGoodsType(goodsType);
GoodsDAO goodsDAO = new GoodsDAO(); Transaction tran = HibernateSessionFactory.getSession().getTransaction(); tran.begin(); goodsDAO.save(goods); tran.commit(); HibernateSessionFactory.closeSession();
System.out.println(goods.getGoodsId()); |
2保存一个新商品,属于现在没有一个商品类型
使用程序的方式维护新的商品与新的商品类型之间的关系
//2 保存一个新商品,属于现在没有一个商品类型(箱包饰品) Goods goods = new Goods(); goods.setGoodsDate(DateUtil.getTimeStamp()); goods.setGoodsImg("wsc.jpg"); goods.setGoodsName("包(粉红版)"); goods.setGoodsPrice(100F); goods.setGoodsNum(1); //商品的级联关系对象商品类型,如果是数据库中不存在的,新保存类型 GoodsType goodsType = new GoodsType(); goodsType.setTypeName("箱包饰品"); goods.setGoodsType(goodsType);//关联商品与类型之间的关系
GoodsTypeDAO goodsTypeDAO = new GoodsTypeDAO(); GoodsDAO goodsDAO = new GoodsDAO(); Transaction tran = HibernateSessionFactory.getSession().getTransaction(); tran.begin(); goodsTypeDAO.save(goodsType); goodsDAO.save(goods); tran.commit(); HibernateSessionFactory.closeSession();
System.out.println(goods.getGoodsId()); |
通过Hibernate维护新的商品与新的商品类型之间的关系
<many-to-one name="goodsType" class="com.no8.domain.GoodsType" lazy="proxy" fetch="select" cascade="save-update" > |
//2 保存一个新商品,属于现在没有一个商品类型(箱包饰品) Goods goods = new Goods(); goods.setGoodsDate(DateUtil.getTimeStamp()); goods.setGoodsImg("wsc.jpg"); goods.setGoodsName("包(粉红版)"); goods.setGoodsPrice(100F); goods.setGoodsNum(1); //商品的级联关系对象商品类型,如果是数据库中不存在的,新保存类型 GoodsType goodsType = new GoodsType(); goodsType.setTypeName("箱包饰品"); goods.setGoodsType(goodsType);//关联商品与类型之间的关系
GoodsDAO goodsDAO = new GoodsDAO(); Transaction tran = HibernateSessionFactory.getSession().getTransaction(); tran.begin(); goodsDAO.save(goods); tran.commit(); HibernateSessionFactory.closeSession();
System.out.println(goods.getGoodsId()); |
17.6.2 修改一个商品信息
修改一个商品的基本属性(goodsName,goodsPrice)与关联关系有没有影响。
修改一个商品的所属类型时才会有关联关系对象有关。
将一个商品(id=83)从A类型(24箱包)修改为B类型(3服饰)
public static void main(String[] args) { //将一个商品(id=86)从A类型(24箱包)修改为B类型(3服饰) GoodsDAO goodsDAO = new GoodsDAO(); GoodsTypeDAO goodsTypeDAO = new GoodsTypeDAO();
Goods goods = goodsDAO.findById(86); // GoodsType old = goods.getGoodsType(); // GoodsType old1 = goodsTypeDAO.findById(24); GoodsType goodsType = goodsTypeDAO.findById(3); HibernateSessionFactory.getSession().getTransaction().begin(); goods.setGoodsType(goodsType); HibernateSessionFactory.getSession().getTransaction().commit();
HibernateSessionFactory.closeSession(); } |
17.6.3 查询商品,按关联关系对象做为查询条件
1查询价格在100以上的数码产品
String hql = "from Goods where goodsPrice >= :goodsPrice" + " and goodsType.typeName = :typeName"; Query query = HibernateSessionFactory.getSession().createQuery(hql); query.setFloat("goodsPrice", 100); query.setString("typeName", "数码产品"); List<Goods> goodsList = query.list();
for (Goods goods : goodsList) { System.out.println(goods.getGoodsName() + ":" + goods.getGoodsPrice() + ":" + goods.getGoodsType().getTypeName()); } HibernateSessionFactory.closeSession(); |
18 练习:后台商品管理模块
18.1 获得记录总数量的方法
public int getCount(SearchVO searchVO){ log.debug("getCount"); try { Map<String,Object> map = new HashMap<String, Object>(); String hql = "select count(*) from Goods where 1=1 "; if(searchVO.getGoodsName()!=null){ hql += "and goodsName like :goodsName "; map.put("goodsName", "%"+searchVO.getGoodsName()+"%"); } if(searchVO.getMinPrice()!=null){ hql += "and goodsPrice >= :min "; map.put("min", searchVO.getMinPrice()); } if(searchVO.getMaxPrice()!=null){ hql += "and goodsPrice <= :max "; map.put("max", searchVO.getMaxPrice()); } if(searchVO.getTypeId()!=null && searchVO.getTypeId() != -1){ hql +="and goodsType.typeId = :typeId "; map.put("typeId", searchVO.getTypeId()); } Query query = getSession().createQuery(hql); //参数设置,为参数赋值。 query.setProperties(map); return (Integer) query.uniqueResult(); } catch (RuntimeException re) { log.error("get failed", re); throw re; } } |
18.2 获得当前页的集合
public List<Goods> search(int page, int recordOfPage, SearchVO searchVO){ log.debug("search"); try { Map<String,Object> map = new HashMap<String, Object>(); String hql = "from Goods where 1=1 "; if(searchVO.getGoodsName()!=null){ hql += "and goodsName like :goodsName "; map.put("goodsName", "%"+searchVO.getGoodsName()+"%"); } if(searchVO.getMinPrice()!=null){ hql += "and goodsPrice >= :min "; map.put("min", searchVO.getMinPrice()); } if(searchVO.getMaxPrice()!=null){ hql += "and goodsPrice <= :max "; map.put("max", searchVO.getMaxPrice()); } if(searchVO.getTypeId()!=null && searchVO.getTypeId() != -1){ hql +="and goodsType.typeId = :typeId "; map.put("typeId", searchVO.getTypeId()); } Query query = getSession().createQuery(hql); //参数设置,为参数赋值。 query.setProperties(map); //设置分页的两个参数 query.setMaxResults(recordOfPage);//每页的记录数量 query.setFirstResult((page-1)*recordOfPage);//当前页的启始索引 return query.list(); } catch (RuntimeException re) { log.error("get failed", re); throw re; } } |
18.3 按商品名称查询商品对象
public Goods findByGoodsName(String goodsName) { log.debug("finding Goods instance with property: goodsName, value: " + goodsName); try { String queryString = "from Goods where goodsName= :goodsName"; Query queryObject = getSession().createQuery(queryString); queryObject.setString("goodsName", goodsName); return (Goods) queryObject.uniqueResult(); } catch (RuntimeException re) { log.error("find by property name failed", re); throw re; } } |
18.4 Service类中一定要将所有关闭全部删除。编写OpenSessionInViewFilter
18.5 Serivce类中要将事务修改为Hibernate的事务处理
public boolean save(Goods goods) { GoodsDAO goodsDAO = new GoodsDAO(); Transaction tran = HibernateSessionFactory.getSession() .getTransaction(); try { tran.begin(); goodsDAO.save(goods); tran.commit(); return true; } catch (Exception e) { e.printStackTrace(); tran.rollback(); return false; } } |
19 双向一对多映射(核心)
双向一对多映射就是要同时维护商品到类型的多对一,和类型到商品的一对多。
在数据库中商品与类型之前是多对一的关系。类型与商品是一对多关系。
同时映射一对多 和 多对一。
19.1 数据库模型
其中:goods.goods_type是外键字段。
19.2 生成实体类与ORM文件
19.2.1 反向生成时的设置选项
19.2.2 Goods类和GoodsType类
商品类,在这个类中应该有“多对一”的关联关系映射->商品类型的对象。
商品类型类,在这个类中应该有“一对多”的关联关系映射->商品的集合
Goods.hbm.xml文件
在这个文件里应该有一个“many-to-one”去描述关联关系
GoodsType.hbm.xml文件
在这个文件里应该有一个“one-to-many”去描述关联关系
19.3 <set inverse="true|false" 属性
控制权反转
inverse="true" : 表示放弃控制权
inverse="false":表示不放弃控制权
现在我们是双向映射。同时维护两边的关系。
两边关系的确定,最终在数据库中只有一个字段。goods表中的goods_type字段。
java程序中,表示商品与类型之间关系的有二个关联关系属性
1Goods类中有一个GoodsType goodsType 的对象属性。
2GoodsType类中有一个Set<Goods> goodses 的集合属性。
这二个关联关系属性同时描述了商品与类型之间的同一个关系。
GoodsTypea = new GoodsType(1);
GoodsTypeb = new GoodsType(2);
Goodsg = new Goods();
a.getGoodses().add(g); goods_type = 1
g.setGoodsType(b); goods_type = 2
<set name="goodses" inverse="true">
在set节点上inverse="true",表示goodses这个集合放弃控制权。
对这个集合的操作将不在影响数据库中表示关系的字段的值。
19.4 基于双向一对多映射的CRUD操作
19.4.1 在goodses的集合上设置inverse="true",保存时与单向多对一操作一致。
19.4.2 查询一个类型,或以同时将类型的商品级联加载。
public static void main(String[] args) { GoodsTypeDAO goodsTypeDAO = new GoodsTypeDAO(); GoodsType goodsType = goodsTypeDAO.findById(1); System.out.println(goodsType.getTypeName()); Set<Goods> goodsSet = goodsType.getGoodses(); for (Goods goods : goodsSet) { System.out.println(goods.getGoodsName()); } HibernateSessionFactory.closeSession(); } |
19.4.3 在一对多映射时的HQL语句的格式
hql有一个叫size()的方法。计算大小。
//查询所有商品数量大于3的类型。
// String hql = "from GoodsType where size(goodses) > 3"; String hql = "from GoodsType where goodses.size > 3"; Query query = HibernateSessionFactory.getSession().createQuery(hql); List<GoodsType> goodsTypeList = query.list(); for (GoodsType goodsType : goodsTypeList) { System.out.println(goodsType.getTypeName()); } HibernateSessionFactory.closeSession(); |
19.5 在一对多映射时cascade的作用
cascade = 级联策略,表示操作当前对象时如何操作级联关系对象。
1save
2save-update
3all
19.5.1 数据库模型,加入一个imgs表。
一个商品除了在goods表中有一个图片,还可以有很多详情的图片。
19.5.2 反向生成
19.5.3 在保存商品同时保存商品图片
public static void main(String[] args) { GoodsType goodsType = new GoodsTypeDAO().findById(21); //新建一个商品 Goods goods = new Goods(); goods.setGoodsName("拿铁"); goods.setGoodsImg("/wsc.jpg"); goods.setGoodsPrice(4.0F); goods.setGoodsNum(10); goods.setGoodsType(goodsType); //新建二个图片 GoodsImg img1 = new GoodsImg(); img1.setImgPath("img1.jpg"); GoodsImg img2 = new GoodsImg(); img2.setImgPath("img2.jpg");
//维护商品与图片之间的关系 // goods.getGoodsImgs().add(img1); // goods.getGoodsImgs().add(img2); img1.setGoods(goods); img2.setGoods(goods);
Transaction tran = HibernateSessionFactory.getSession().getTransaction(); tran.begin(); new GoodsDAO().save(goods); new GoodsImgDAO().save(img1); new GoodsImgDAO().save(img2); tran.commit(); HibernateSessionFactory.closeSession();
System.out.println(goods.getGoodsId()); System.out.println("end"); } |
public static void main(String[] args) { GoodsType goodsType = new GoodsTypeDAO().findById(21); //新建一个商品 Goods goods = new Goods(); goods.setGoodsName("拿铁"); goods.setGoodsImg("/wsc.jpg"); goods.setGoodsPrice(4.0F); goods.setGoodsNum(10); goods.setGoodsType(goodsType); //新建二个图片 GoodsImg img1 = new GoodsImg(); img1.setImgPath("img1.jpg"); GoodsImg img2 = new GoodsImg(); img2.setImgPath("img2.jpg");
//维护商品与图片之间的关系 goods.getGoodsImgs().add(img1); goods.getGoodsImgs().add(img2); img1.setGoods(goods); img2.setGoods(goods);
Transaction tran = HibernateSessionFactory.getSession().getTransaction(); tran.begin(); new GoodsDAO().save(goods); // new GoodsImgDAO().save(img1); // new GoodsImgDAO().save(img2); tran.commit(); HibernateSessionFactory.closeSession();
System.out.println(goods.getGoodsId()); System.out.println("end"); } |
20 案例20:保存商品同时保存多张图片
20.1 数据库模型
20.2 静态模型
20.3 Servlet控制器-SaveGoodsController.java
//要保存的商品对象 Goods goods = new Goods(); // 批量文件上传 List<Part> parts = (List<Part>) request.getParts(); for (Part part : parts) { if("goodsImgs".equals(part.getName())){ //单一文件上传 String fileRealName = FileNameUtil.getRealFileName(part .getHeader("Content-Disposition")); String fileSaveName = FileNameUtil.getUUIDFileName() + FileNameUtil.getFileType(fileRealName); String path = request.getServletContext().getRealPath("/upimgs"); part.write(path + "/" + fileSaveName); //一个商品的图片,需要保存到数据库中 GoodsImg goodsImg = new GoodsImg(); goodsImg.setImgPath(fileSaveName); //商品有图片,维护商品与图片的关系 goods.getGoodsImgs().add(goodsImg); //图片属于商品,维护图片与商品的关系 goodsImg.setGoods(goods); } } |
20.4 Service业务层-GoodsService.java
public boolean save(Goods goods) { GoodsDAO goodsDAO = new GoodsDAO(); GoodsImgDAO goodsImgDAO = new GoodsImgDAO(); Transaction tran = HibernateSessionFactory.getSession() .getTransaction(); try { tran.begin(); goodsDAO.save(goods); //当前没有设置Goods到GoodsImg的cascade。所以代码补 Set<GoodsImg> goodsImgSet = goods.getGoodsImgs(); for (GoodsImg goodsImg : goodsImgSet) { goodsImgDAO.save(goodsImg); } tran.commit(); return true; } catch (Exception e) { e.printStackTrace(); tran.rollback(); return false; } } |
21 修改案例20,单独搭建图片上传服务器
@WebListener public class InitAppListener implements ServletContextListener { public void contextInitialized(ServletContextEvent arg0) { // TODO Auto-generated method stub ServletContext context = arg0.getServletContext(); GoodsTypeSerivce goodsTypeSerivce = new GoodsTypeSerivce(); List<GoodsType> goodsTypeList = goodsTypeSerivce.findAll(); context.setAttribute("typeList", goodsTypeList);
String path = context.getRealPath("/"); String imgPath = path.replace("web1012", "uploadimg")+"upimgs"; System.out.println(imgPath); context.setAttribute("imgPath", imgPath); } public void contextDestroyed(ServletContextEvent arg0) { } } |
// 要保存的商品对象 Goods goods = new Goods(); String path = request.getServletContext().getRealPath("/upimgs"); String imgPath = (String) this.getServletContext().getAttribute("imgPath"); // 批量文件上传 List<Part> parts = (List<Part>) request.getParts(); for (Part part : parts) { if ("goodsImgs".equals(part.getName())) { // 单一文件上传 String fileRealName = FileNameUtil.getRealFileName(part .getHeader("Content-Disposition")); String fileSaveName = FileNameUtil.getUUIDFileName() + FileNameUtil.getFileType(fileRealName); part.write(imgPath + "/" + fileSaveName); //... } } |
22 关系型数据库中的多对多映射
22.1 数据库模型中的多对多映射。
在关系型数据库中多对多的关系,必须依赖于第三张表。第三张表称关系表。
关系表中二种情况的方式:有没有业务字段
1有业务字段:商品与订单的多对多关系
商品与订单的关系表:
id(主键),商品id,订单id,订购价格,订购数量(其中订购价格和订购数量就是业务字段)
2没有业务字段:教员与学员的多对多关系
教员与学员的关系表:业务只要求能表示教员与学员之间的关系。
教员id,学员id
23 使用Hibernate映射-有业务字段的多对多
23.1 数据库模型
在关系型数据库,将订单与商品的多对多关系拆分成了二个多对一关系。
23.2 生成实体类和ORM文件
针对关系表中有业务字段的,在Hibernate中就是将多对多映射拆分成了二个双向一对多映射。
Goods.java
Orders.java
OrderDetails.java
23.3 生成订单功能,实现保存订单时同时保存订单明细
23.3.1 显示所有商品信息
23.3.2 显示商品详情信息
23.3.3 购物车
23.3.4 生成订单
public class OrderService { private OrdersDAO ordersDAO = new OrdersDAO(); private OrderDetailsDAO orderDetailsDAO = new OrderDetailsDAO(); private GoodsDAO goodsDAO = new GoodsDAO();
public boolean save(Orders order) { Transaction tran = HibernateSessionFactory.getSession() .getTransaction(); try { tran.begin(); ordersDAO.save(order); Set<OrderDetails> detailsSet = order.getOrderDetailses(); for (OrderDetails orderDetails : detailsSet) { orderDetailsDAO.save(orderDetails); Goods goods = goodsDAO.findById(orderDetails.getGoods() .getGoodsId()); goods.setGoodsNum(goods.getGoodsNum() - orderDetails.getDetailNum()); } tran.commit(); return true; } catch (Exception e) { e.printStackTrace(); tran.rollback(); return false; } } } |
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1 接收用户请求信息 String orderName = request.getParameter("orderName"); String orderPhone = request.getParameter("orderPhone"); String orderAddress = request.getParameter("orderAddress");
// 2 调用业务逻辑 // 获得当前登录用户 Users user = new UserService().findById(1);
Orders order = new Orders(); order.setOrderId(FileNameUtil.getUUIDFileName()); order.setOrderName(orderName); order.setOrderPhone(orderPhone); order.setOrderAddress(orderAddress); order.setUsers(user);
// 获得购物车中的商品 HttpSession session = request.getSession(); Map<Integer, ItemVO> shopcar = (Map<Integer, ItemVO>) session .getAttribute("shopcar"); Set<Entry<Integer, ItemVO>> entrySet = shopcar.entrySet(); for (Entry<Integer, ItemVO> entry : entrySet) { OrderDetails details = new OrderDetails(); details.setDetailNum(entry.getValue().getNum()); details.setDetailPrice(entry.getValue().getGoods().getGoodsPrice()); details.setOrders(order);// 明细属于哪个订单 details.setGoods(entry.getValue().getGoods());// 明细属于哪个商品 order.getOrderDetailses().add(details);// 订单知道有明细 }
OrderService service = new OrderService(); if (service.save(order)) { // 清空购物车 // 显示订单详情页面 response.sendRedirect(request.getContextPath() + "/order/findbyid.do?orderId=" + order.getOrderId()); } else { response.sendRedirect(request.getContextPath()+"/car.jsp"); }
} |
23.3.5 显示订单详情
24 使用Hibernate映射-没有业务字段的多对多映射
24.1 数据库模型
24.2 反向生成实体类与ORM文件
Teacher.java
public class Teacher implements java.io.Serializable { // Fields private Integer teaId; private String teaName; private Set students = new HashSet(0); |
Student.java
public class Student implements java.io.Serializable { // Fields private Integer stuId; private String stuName; private Set teachers = new HashSet(0); |
当hibernate映射 多对多(many-to-many) , 只用二个实体类就够了。
但是在映射过程中,必须有一方负责维护二者之间的关系。
二者之间的关系是:数据库中的关系表。
现在在当前Student和Teacher中,哪个类有对关系的控制能力?Teacher类可以控制关系。
Teacher类的哪个属性控制?Teacher类中的Set students(学员集合)属性。
24.3 映射关系的维护
案例1:增加一个新的学员(王瑞),做为编号1,孙老师的学员。
public static void main(String[] args) { //案例1:增加一个新的学员(王瑞),做为编号1,孙老师的学员。 Student s = new Student(); s.setStuName("王琳琳"); Teacher t = new TeacherDAO().findById(1); //关系 t.getStudents().add(s); s.getTeachers().add(t); Transaction tran = HibernateSessionFactory.getSession().getTransaction(); tran.begin(); new StudentDAO().save(s); tran.commit();
HibernateSessionFactory.closeSession(); System.out.println("end!"); } |
案例2:将一个学员(编写6, 王梦),从孙老师(1)移到张老师(2)
public static void main(String[] args) { //案例2:将一个学员(编写6, 王梦),从孙老师(1)移到张老师(2) Student s = new StudentDAO().findById(6); Teacher t1 = new TeacherDAO().findById(1); Teacher t2 = new TeacherDAO().findById(2);
HibernateSessionFactory.getSession().getTransaction().begin(); s.getTeachers().remove(t2); t2.getStudents().remove(s); s.getTeachers().add(t1); t1.getStudents().add(s);
HibernateSessionFactory.getSession().getTransaction().commit();
HibernateSessionFactory.closeSession();
System.out.println("end!"); } |
25 自连接映射
自连接:就是一张表中的字段做为外键,引用自己表中的主键。
商品类型表中使用自连接。
25.1 数据库模型
25.2 生成实体类与ORM文件
25.3 保存一个第一级的商品类型
public static void main(String[] args) { //保存第一级类型 GoodsType goodsType = new GoodsType(); goodsType.setTypeName("书籍资料"); goodsType.setTypeLv(1);
Transaction tran = HibernateSessionFactory.getSession().getTransaction(); tran.begin(); new GoodsTypeDAO().save(goodsType);//保存操作,对象状态修改为持久态,主键赋值。 goodsType.setTypePath("|"+goodsType.getTypeId()+"|");//修改类型的path属性。 tran.commit();
HibernateSessionFactory.closeSession(); System.out.println("end"); } |
25.4 在25.书籍资料下加入一个新的子类型
public static void main(String[] args) { // 在25.书籍资料下加入一个新的子类型 GoodsType pgoodstype = new GoodsTypeDAO().findById(25);
GoodsType goodsType = new GoodsType(); goodsType.setGoodsType(pgoodstype);// 设置当前类型的父类型。 // pgoodstype.getGoodsTypes().add(goodsType); goodsType.setTypeName("英语书籍"); goodsType.setTypeLv(pgoodstype.getTypeLv() + 1);
Transaction tran = HibernateSessionFactory.getSession() .getTransaction(); tran.begin(); // 保存操作,对象状态修改为持久态,主键赋值。 new GoodsTypeDAO().save(goodsType); // 修改类型的path属性。 goodsType.setTypePath(pgoodstype.getTypePath() + "|" + goodsType.getTypeId() + "|"); tran.commit();
HibernateSessionFactory.closeSession();
System.out.println("end"); } |
25.5 查询一个类型(4 照相机),要显示这个类型的父类型和所有子类型
public static void main(String[] args) { //25.5 查询一个类型(4 照相机),要显示这个类型的父类型和所有子类型 GoodsType goodstype = new GoodsTypeDAO().findById(4);
System.out.println("显示当前类型节点的信息"); System.out.println(goodstype.getTypeName()); System.out.println(goodstype.getTypeLv()); System.out.println("显示父类型节点的信息"); System.out.println(goodstype.getGoodsType().getTypeName()); System.out.println(goodstype.getGoodsType().getTypeLv()); System.out.println("显示所有子类型节点的信息"); Set<GoodsType> goodsTypeSet = goodstype.getGoodsTypes(); for (GoodsType type : goodsTypeSet) { System.out.println(type.getTypeName()); System.out.println(type.getTypeLv()); System.out.println("----------"); }
HibernateSessionFactory.closeSession(); System.out.println("end"); } |
25.6 查询所有属于商品类型(3 服装服饰)的商品信息
public static void main(String[] args) { //25.6 查询所有属于商品类型(3 服装服饰)的商品信息 int typeId = 3;//查询商品的类型编号 // GoodsType goodsType = new GoodsTypeDAO().findById(typeId); // System.out.println(goodsType.getGoodses().size()); String hql = "from Goods g where g.goodsType.typePath like :typeId"; Query query = HibernateSessionFactory.getSession().createQuery(hql); query.setString("typeId", "%|"+typeId+"|%"); System.out.println(query.list()); System.out.println(query.list().size()); HibernateSessionFactory.closeSession(); System.out.println("end"); } |
26 QBC-按条件查询
Hiernate查询:
1成生DAO。
2Session对象的load方法,和get方法。
3 HQL(使用比较多,与SQL语句很像)
4 QBC和QBE(面向对象的查询方式,推荐使用)
5 SQL-原生的SQL语句
Query、Criteria、SqlQuery这三个接口
Criteria本身只是个查询容器,具体的查询条件需要通过Cretiria.add方法添加Criterion到Criteria实例中。
Restrictions的一系列静态方法可以创建Criterion对象,
Criterion具体描述查询条件,针对SQL语法,Criterion提供了对应的查询限定机制。
26.1 获得Criteria接口
与Query接口的方式一致,通过Session的方法获得Criteria接口的实例。
public void qbc01(){ Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); } |
26.2 基础查询-查询所有
//查询所有商品 public void qbc02(){ Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); List<Goods> goodsList = criteria.list(); for (Goods goods : goodsList) { System.out.println(goods.getGoodsName()); System.out.println(goods.getGoodsType().getTypeName()); System.out.println("---------"); } HibernateSessionFactory.closeSession(); } |
26.3 条件查询
Criteria本身只是个查询容器,具体的查询条件需要通过Cretiria.add方法添加Criterion到Criteria实例中。
Criterion具体描述查询条件,针对SQL语法,Criterion提供了对应的查询限定机制
Restrictions的一系列静态方法可以创建Criterion对象
26.3.1 Restrictions的一系列静态方法如下:
26.3.2 基础条件查询
查询商品价格大于200的商品
@Test //查询商品价格大于200的商品 public void qbc03(){ Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //创建的一个查询条件对象 Criterion criterion = Restrictions.gt("goodsPrice", 2000f); criteria.add(criterion); List<Goods> goodsList = criteria.list(); for (Goods goods : goodsList) { System.out.println(goods.getGoodsName()); System.out.println(goods.getGoodsPrice()); System.out.println(goods.getGoodsType().getTypeName()); System.out.println("---------"); } HibernateSessionFactory.closeSession(); } |
26.3.3 between...and...
public void qbc03(){ Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //创建的一个查询条件对象 Criterion criterion = Restrictions.between("goodsPrice", 2000f, 3000f); criteria.add(criterion); List<Goods> goodsList = criteria.list(); for (Goods goods : goodsList) { System.out.println(goods.getGoodsName()); System.out.println(goods.getGoodsPrice()); System.out.println(goods.getGoodsType().getTypeName()); System.out.println("---------"); } HibernateSessionFactory.closeSession(); } |
26.3.4 like 模糊查询
查询所有类型属于3、服装服饰的商品类型
@Test //查询所有类型属于3、服装服饰的商品类型 public void qbc04(){ Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(GoodsType.class); //创建的一个查询条件对象 Criterion criterion = Restrictions.like("typePath", "|3|", MatchMode.ANYWHERE); criteria.add(criterion); List<GoodsType> goodsTypeList = criteria.list(); for (GoodsType goodsType : goodsTypeList) { System.out.println(goodsType.getTypeId()); System.out.println(goodsType.getTypeName()); } HibernateSessionFactory.closeSession(); } |
26.3.5 in (...)
查询所有抽奖的商品(1,5,8,9)
@Test //查询所有抽奖的商品(1,5,8,9) public void qbc05(){ List<Integer> goodsIds = new ArrayList<Integer>(); goodsIds.add(1); goodsIds.add(5); goodsIds.add(8); goodsIds.add(9); Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //创建的一个查询条件对象 Criterion criterion = Restrictions.in("goodsId", goodsIds); criteria.add(criterion); List<Goods> goodsList = criteria.list(); for (Goods goods : goodsList) { System.out.println(goods.getGoodsName()); System.out.println(goods.getGoodsPrice()); System.out.println(goods.getGoodsType().getTypeName()); System.out.println("---------"); } HibernateSessionFactory.closeSession(); } |
26.4 and 与
在Hibernate中有三种方式可以实现“与”条件
26.4.1 使用多个criteria.add方法,每个条件就是“与”关系
查询价格大2000并数量大100
@Test //查询价格大2000并数量大100 public void qbc06(){ Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //创建的一个查询条件对象 Criterion criterion1 = Restrictions.gt("goodsPrice", 2000f); Criterion criterion2 = Restrictions.lt("goodsNum", 100); //将条件以“与”的方式连接起来 criteria.add(criterion1).add(criterion2); List<Goods> goodsList = criteria.list(); for (Goods goods : goodsList) { System.out.println(goods.getGoodsName()); System.out.println(goods.getGoodsPrice()); System.out.println(goods.getGoodsType().getTypeName()); System.out.println("---------"); } HibernateSessionFactory.closeSession(); } |
26.4.2 使用Restrictions.add方法连接多个“与”运算的条件
@Test //查询价格大2000并数量小100 public void qbc07(){ Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //创建的一个查询条件对象 Criterion criterion1 = Restrictions.gt("goodsPrice", 2000f); Criterion criterion2 = Restrictions.lt("goodsNum", 100); //创建了一个“与”运算的查询条件 Criterion criterion3 = Restrictions.and(criterion1,criterion2); criteria.add(criterion3); //执行查询 List<Goods> goodsList = criteria.list(); for (Goods goods : goodsList) { System.out.println(goods.getGoodsName()); System.out.println(goods.getGoodsPrice()); System.out.println(goods.getGoodsType().getTypeName()); System.out.println("---------"); } HibernateSessionFactory.closeSession(); } |
26.4.3 使用Restrictions.conjunction()获得一个“与”运算条件对象(个人推荐)
Conjunctionconjunction = Restrictions.conjunction()
conjunction.add(Criterion);
conjunction.add(Criterion);
conjunction.add(Criterion);
@Test //查询价格大2000并数量小100 public void qbc07(){ Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //创建的一个查询条件对象 Criterion criterion1 = Restrictions.gt("goodsPrice", 2000f); Criterion criterion2 = Restrictions.lt("goodsNum", 100); //创建了一个“与”运算的查询条件 Conjunction conjunction = Restrictions.conjunction(); conjunction.add(criterion1); conjunction.add(criterion2); //将“与”运算的查询条件加到查询对象中 criteria.add(conjunction); //执行查询 List<Goods> goodsList = criteria.list(); for (Goods goods : goodsList) { System.out.println(goods.getGoodsName()); System.out.println(goods.getGoodsPrice()); System.out.println(goods.getGoodsType().getTypeName()); System.out.println("---------"); } HibernateSessionFactory.closeSession(); } |
26.5 or 或
在Hibernate的QBC中只有二种实现方式:
26.5.1 使用Restrictions.or方法
@Test //查询价格小2000或数量大于100 public void qbc08(){ Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //创建的一个查询条件对象 Criterion criterion1 = Restrictions.lt("goodsPrice", 2000f); Criterion criterion2 = Restrictions.gt("goodsNum", 100); Criterion criterion3 = Restrictions.or(criterion1,criterion2); criteria.add(criterion3); //执行查询 List<Goods> goodsList = criteria.list(); HibernateSessionFactory.closeSession(); } |
26.5.2 使用Restrictions.disjunction()实现“或”关系的运算
@Test //查询价格大2000或数量小100 public void qbc09(){ Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //创建的一个查询条件对象 Criterion criterion1 = Restrictions.gt("goodsPrice", 2000f); Criterion criterion2 = Restrictions.lt("goodsNum", 100); //创建了一个“或”运算的查询条件 Disjunction disjunction = Restrictions.disjunction(); disjunction.add(criterion1); disjunction.add(criterion2); //将“与”运算的查询条件加到查询对象中 criteria.add(disjunction); //执行查询 List<Goods> goodsList = criteria.list(); HibernateSessionFactory.closeSession(); } |
26.6 关联关系查询
createCriteria创建关联Criteria
createAlias创建关联对象的别名
26.6.1 X对一
查询商品,条件商品类型=“家用电器”并价格大于1000
sql : select * from goods inner joingoods_type on goods.goods_type = goods_type.type_id
wheregoods_type.type_name = '家用电器' and goods.goods_price > 1000
hql : from Goods g where g.goodsPrice >1000 and g.goodsType.typeName = '家用电器'
QBC: 两种方式:
1使用createCriteria为关联关系对象创建一个新查询
public void qbc10() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); criteria.add(Restrictions.gt("goodsPrice", 1000f)); criteria.createCriteria("goodsType") .add(Restrictions.eq("typeName", "家用电器")); // 执行查询 List<Goods> goodsList = criteria.list(); HibernateSessionFactory.closeSession(); } |
2使用createAlias为关联关系对象创建一个别名(实例名称)
public void qbc11() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class,"g"); criteria.createAlias("g.goodsType", "t");//就是给关联关系对象启名 criteria.add(Restrictions.gt("g.goodsPrice", 1000f)); criteria.add(Restrictions.eq("t.typeName", "家用电器")); // 执行查询 List<Goods> goodsList = criteria.list(); HibernateSessionFactory.closeSession(); } |
26.6.2 X对多
查询商品数量大于3个的商品类型
//查询商品数量大于3个的商品类型 public void qbc12() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(GoodsType.class); criteria.add(Restrictions.sizeGt("goodses", 3)); // 执行查询 List<GoodsType> goodsTypeList = criteria.list(); HibernateSessionFactory.closeSession(); } |
查询商品价格在3000以上的商品类型
1使用关联查询
@Test // 查询商品价格在3000以上的商品类型 public void qbc13() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(GoodsType.class); criteria.createCriteria("goodses").add( Restrictions.gt("goodsPrice", 3000f)); //去重 criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); // 执行查询 List<GoodsType> goodsTypeList = criteria.list(); for (GoodsType goodsType : goodsTypeList) { System.out.println(goodsType.getTypeName()); } HibernateSessionFactory.closeSession(); } |
2使用别名
@Test // 查询商品价格在3000以上的商品类型 public void qbc14() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(GoodsType.class); criteria.createAlias("goodses","g").add( Restrictions.gt("g.goodsPrice", 3000f)); //去重 criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); // 执行查询 List<GoodsType> goodsTypeList = criteria.list(); for (GoodsType goodsType : goodsTypeList) { System.out.println(goodsType.getTypeName()); } HibernateSessionFactory.closeSession(); } |
26.7 投影查询
投影查询:只查询表中的某几列
26.7.1 单列的投影查询
单列的投影查询时,因为查询结果只有一列,如果结果为多行,返回类型就是一个集合。
但因为是一列,所以集合的泛型就是这个列的类型。
@Test // 单列的投影查询 select goods_name from goods; public void qbc15() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //设置投影的列 criteria.setProjection(Projections.property("goodsName")); //查询,只有一列,集合泛型就是这个列的类型 List<String> goodsNameList = criteria.list(); for (String goodsName : goodsNameList) { System.out.println(goodsName); } HibernateSessionFactory.closeSession(); } |
26.7.2 多列的投影查询
多列投影查询因为查询的属性是多个,返回集合的泛型应该是Object[]
@Test // 多列的投影查询 select goods_name,goods_price from goods; public void qbc16() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //设置投影的列 criteria.setProjection( Projections.projectionList() .add(Projections.property("goodsName")) .add(Projections.property("goodsPrice")) ); //查询,只有多列,集合泛型就是Object[] List<Object[]> goodsList = criteria.list(); for (Object[] objects : goodsList) { for (Object object : objects) { System.out.println(object); } System.out.println("----------"); } HibernateSessionFactory.closeSession(); } |
在讲hql语句时,就可以将Object[]换成Map集合,在QBC里面也可以将Object[]换成Map集合
1在调用add方法时一定要设置别名
.add(Projections.property("goodsName"),"name")
2在查询之前 设置 将别名映射成Map
criteria.setResultTransformer(criteria.ALIAS_TO_ENTITY_MAP);
@Test // 多列的投影查询 select goods_name,goods_price from goods; public void qbc17() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //设置投影的列时,在调用add方法时一定要为列设置别名 criteria.setProjection( Projections.projectionList() .add(Projections.property("goodsName"),"name") .add(Projections.property("goodsPrice"),"price") ); //设置一个参数,将多列投影查询映射成Map集合 criteria.setResultTransformer(criteria.ALIAS_TO_ENTITY_MAP); //查询,集合泛型就是Map<String,Object> List<Map<String, Object>> goodsList = criteria.list(); for (Map<String, Object> map : goodsList) { System.out.println(map.get("name")); System.out.println(map.get("price")); System.out.println("-------------"); } HibernateSessionFactory.closeSession(); } |
26.7.3 投影查询支持SQL运算
sql:selectgoods_price * goods_num as sumprice from goods;
@Test // sql:select goods_name,goods_price*goods_num as sumprice from goods; public void qbc18() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //设置投影的列时,在调用add方法时一定要为列设置别名 criteria.setProjection( Projections.projectionList() .add(Projections.property("goodsName"),"name") .add(Projections.sqlProjection( "goods_price*goods_num as sumprice", new String[]{"sumprice"}, new Type[]{new DoubleType()})) ); //设置一个参数,将多列投影查询映射成Map集合 criteria.setResultTransformer(criteria.ALIAS_TO_ENTITY_MAP); //查询,集合泛型就是Map<String,Object> List<Map<String, Object>> goodsList = criteria.list(); for (Map<String, Object> map : goodsList) { System.out.println(map.get("name")); System.out.println(map.get("sumprice")); System.out.println("-------------"); } HibernateSessionFactory.closeSession(); } |
26.8 聚合函数
count(),sum(),min(),max(),avg()
sql: select count(*) from goods;
@Test // sql:select count(*) from goods; public void qbc19() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); // 设置投影的列时 criteria.setProjection(Projections.rowCount()); // 查询,有聚合函数不分组,一定只有一条记录 Long count = (Long) criteria.uniqueResult(); System.out.println(count); HibernateSessionFactory.closeSession(); }
@Test // sql:select count(goods_img) from goods; public void qbc20() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); // 设置投影的列时 criteria.setProjection(Projections.count("goodsImg")); // 查询,有聚合函数不分组,一定只有一条记录 Long count = (Long) criteria.uniqueResult(); System.out.println(count); HibernateSessionFactory.closeSession(); }
@Test // sql:select max(goods_price) as maxPrice,min(goods_price) from goods; public void qbc21() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(Goods.class); //设置投影的列时 criteria.setProjection( Projections.projectionList() .add(Projections.rowCount(),"count") .add(Projections.max("goodsPrice"),"maxPrice") .add(Projections.min("goodsPrice"),"minPrice") ); criteria.setResultTransformer(criteria.ALIAS_TO_ENTITY_MAP); //查询,有聚合函数不分组,一定只有一条记录 Map<String, Object> map = (Map<String, Object>) criteria.uniqueResult(); System.out.println(map.get("count")); System.out.println(map.get("maxPrice")); System.out.println(map.get("minPrice")); HibernateSessionFactory.closeSession(); } |
26.9 分组查询
统计每个商品的销售情况,包含销售总数,销售总额。
销售情况在哪张表?订单明细表
每个商品:按商品分组
销售总数:sum(销售数量)
销售总额:sum(数量*单价)
SQL:select sum(detail_num),sum(detail_num*detail_price) fromorderdetails group by detail_goods;
@Test //sql: // select detail_goods,sum(detail_num),sum(detail_num*detail_price) // from order_details group by detail_goods public void qbc22() { Session session = HibernateSessionFactory.getSession(); Criteria criteria = session.createCriteria(OrderDetails.class); // 设置投影的列时 criteria.setProjection(Projections.projectionList() .add(Projections.property("goods"),"goods") .add(Projections.sum("detailNum"), "sumNum") .add(Projections.sqlProjection("sum(detail_num*detail_price) as sumPrice", new String[]{"sumPrice"}, new Type[]{new DoubleType()})) .add(Projections.groupProperty("goods")) ); criteria.setResultTransformer(criteria.ALIAS_TO_ENTITY_MAP); // 查询,有聚合函数不分组,一定只有一条记录 List<Map<String, Object>> detailGroupList = criteria.list(); for (Map<String, Object> map : detailGroupList) { Goods goods = (Goods) map.get("goods"); Long sum = (Long) map.get("sumNum"); Double sumPrice = (Double) map.get("sumPrice"); System.out.println(goods.getGoodsName()); System.out.println(sum); System.out.println(sumPrice); System.out.println("----------"); } HibernateSessionFactory.closeSession(); } |
26.10 去重复行
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
查询所有被购买过的商品