Hibernate九阴真经
一、导入jar包。
二、创建实体类(javabean)
*hibernate 要求实体类中必须有一个属性是唯一的
*使用hibernate不需要我们自己手动创建数据库表,hibernate会帮我们把表创建
三、配置实体类与数据库表的对应关系(ORM映射 )
1、创建xml格式的配置文件
映射配置文件和名称没有固定要求,建议:实体类所在包里面创建,实体类名称.xml
2、配置是xml格式,在配置文件中首先引入xml约束
学过约束dtd/schema,在hibernate中都是用dtd;
3、配置映射关系
<hibernate-mapping>
<class name=”实体类全路径” table=”数据库表名称”>配置表与对象的对应关系
<id name=”实体类的唯一属性” column=”数据库表中的对应列名”>
<generator class=”native”></generator>配置自增长属性
</id>
<property name=”实体类属性” column=”数据库表列名”></property>注意属性名与列名要一一对应,必须相同名,column可以省略默认与属性名一样;type=”表中字段类型varchar等不写默认与属性类型相同”;
<property name=”实体类属性” column=”数据库表列名”></property>
……
</class>
</hibernate-mapping>
四、创建hibernate核心配置文件
1、位置:必须在src下
名称:固定hibernate.cfg.xml
2、引入约束
3、hibernate操作过程中,只会加载核心配置文件,其他配置文件不会加载
(1)配置数据库信息
(2)配置hibernate信息
(3)把映射文件放到核心配置文件中
<hibernate-configuration>
<session-factory>
配置数据库信息(必须)
<property name=”hibernate.connection.diver.class”>com.mysql.jdbc.Driver</property>
<property name=”hibernate.connection.url”>jdbc:mysql:///hibernate_01</property>
<property name=”hibernate.connection.username”>root</property>
<property name=”hibernate.connection.password”>123</property>
……(配置数据库四大参数)
配置hibernate信息(可选)
<property name=”hibernate.show_sql”>true</property> 输出底层sql语句
<property name=”hibernate.formate_sql”>true</property>输出sql语句格式
<property name=”hibernate.hbm2ddl.auto”>update</property>自动创建表,update:有则更新,无则创建;
<property name=”hibernate.dialect”>org.hibernate.dialect.MySQLDialect</property>配置数据库方言,例如在mysql中使用limit/oracle中使用rownum进行分页
把映射文件放到核心配置文件中(必须)
<mapping resource=”映射配置文件的全路径”/>
</session-factory>
</hibernate-configuration>
五、代码逻辑
1、加载配置文件
Configuration conf = new Configuration();
conf.configure();
调用方法后src下hibernate核心配置文件就被加载完成
2、创建SessionFactory(重点)
SessionFactory sf = conf.buildSessionFactory();
根据核心配置文件,有数据库配置,有映射文件部分,到数据库里面根据映射关系创建表
创建过程特别耗损资源,建议一个项目一般创建一个SessionFactory对象,(静态代码块实现);
private static Confiuration conf =null;
private static SessionFactory sf =null;
static{
new Configuration();
conf.confiure();
sf = conf.buildSessionFactory();
}
3、得到Session
Session session = sf.openSession();
理解:类似于jsbc中的connection
调用session里面不同的方法实现crud操作:
(1)添加save();
(2)修改update();
(3)删除delete();
(4)按照id查询get(id);
……
session是单线程对象,每个人只能用自己的(ThreadLocal);
4、Transaction 事物
Transaction tx = session.beginTransaction();
主要方法:tx.commit(); tx.rollback();
?解决配置文件没有提示的问题:
1、可以上网,会自动引入提示;
2、把约束文件引入到eclipse中,window-preferences 搜索xml Catalog 点击add 将配置文件路径复制到 URI(要选)下 ,点击file System 寻找到本地约束文件 ,点击OK结束;重启eclipse;
六、hibernate主键生成策略:
native:代理主键,自动增长;
uuid:hibernate会自动帮我们生成一个32位16进制字符串;
例:实体类中定义唯一属性 private String uid;
映射配置文件给uid设置uuid <generator class=”uuid”></ generator>
七、对实体类的增删改查操作
添加操作:User.setUsername(“xiaoma”); user.setAge(10); session.save();
根据id查询:
1、调用session中的get方法实现;
2、代码如下:
SessionFactory sf = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session beginTransaction();
User user = session.get(User.class,id值);
tx.commit();
session.close();
sf.close();
修改操作:
1、首先查询;
User user = session.get(User.class,2);
2、修改;
user.setUsername(“李连杰”);
session.update(user); 底层是到对象中找到id 然后根据id进行修改
删除操作
1调用delete方法实现一;
User user = session.get(User.class,1);
session.delete(user);
2、调用delete方式实现二:
User user = new User();
user.setUid(1); session.delete(user);
八、实体类对象状态(概念)
1瞬时态:对象里面没有id值,跟session没有关联
2持久态:对象里面有di值并且与session有关联
3托管态:对象里面有id值但是对象跟session没有关联
saveOrUpdate方法:
可以实现添加也能实现修改;瞬时态进行添加,托管态进行修改;持久态进行修改;(有id值就行修改,没id就执行添加)
User user = new User();
user.setUsername(“jack”);
user.setUid(4);
session.saveOrUpdate(user);
九、hibernate的一级缓存
什么是缓存?
数据存到数据库里面,数据库本身就是文件系统,java中使用流来操作文件,效率不高。
(1)把数据存到内存里面,不需要使用流方式,可以直接读取内存中数据
(2)把数据放到内存中,提高读取效率;
hibernate缓存
1hibernate框架中提供很多优化方式,hibernate的缓存就是一个优化方式
2hibernate缓存特点:
(1)hibernate的一级缓存
hibernate的一级缓存默认打开的
hibernate的一级缓存使用范围,是session范围
(2)hibernate的二级缓存
目前不使用,替代技术redis;
默认不打开
范围sessionFactory
验证一级缓存存在
1验证方式
(1)首先根据uid=1查询,返回对象,发送sql语句;
(2)再次根据uid查询,直接返回对象,不执行sql语句;
一级缓存特性:
持久态自动更新数据库,
User user = session.get(User.class,1);
user.setUsername(“hansan”); 不调用update方法也会修改数据库
十、hibernate事物代码规范
事物概念:
1什么是事物?一组操作有一个失败都失败
2事物特性:原子性、一致性、隔离性、持久性
3不考虑隔离性的问题?脏读、不可重复读、虚读
解决办法: 设置事物隔离级别 repeatable read(mysql默认)
事物代码结构:
Transaction tx=null;
try{
tx.beginTransaction();
….操作数据库代码
tx.commit();
}catch(Exception e){
tx.rollBack();
}
十一、Hibernate绑定Session
获取与本地线程绑定的session
1、在hibernate核心配置文件中进行配置:
<property name=”hibernate.current_session_context_class”>thread</property>
2、调用SessionFactory.getCurrentSession();得到与当前线程绑定的session(底层依然是threadlocal)。
3、获取本地线程绑定session,关闭session报错,不需要手动关闭;
十二、查询(所有内容)
api:
Query 对象:
Query q = session.createQuery(“from User”); from+实体类名
List<User> users = q.list();
for(User u:users)(syout(u));
Criteria 对象:
Criteria c =session.createCriteria(User.class);
List<User>users = c.list();
SQLQuery 对象:
1使用hibernate时候,也可以来调用底层sql来实现,而SQLQuery就能编写sql语句;
SQLQuery sql = session.createSQLQuery(“select * from user”);
List<Object[]> users = sql.list();返回的list每个元素是一个数组;
for(Object[] array : users){Arrays.toString(array)}
修改返回数据中每部分是对象而不是数组;
sql.addEntity(User.class);
List<User> users = sql.list();
for(User user : users){sysout(user)}
十三、Hibernate 一对多操作
一对多映射配置:
第一步 创建实体类(例如 商品与分类就是一对多关系)
第二步 让两个实体类之间互相关联 (分类是一的一方,商品是多的一方)
在分类中创建一个set集合,无序、可重复;(Hibernate规定set)
Set<Book> books = new HashSet<Book>();
在商品实体类中 添加
private Category category; 属性以及get /set方法;
第三步 配置映射文件
1、配置好两张表基本的映射关系;
2、映射文件中,配置一对多关系
分类实体类的配置:(分类中表示商品)
<set name=”books”>
<key column=”外键”></key>
<one-to-many class=”被关联实体类的全路径”/>
</set>
商品实体类的配置:(商品中表示所属分类)
<many-to-one name=”category” class=”被关联实体类全路径” column=”外键”></ many-to-one>
第四步 创建核心配置文件,把映射文件引入到核心配置文件
要将两个映射文件都引入到核心配置文件中;
一对多级联操作:
简化写法:
第一步
在映射文件中的set标签中 添加cascade=”save-update”
<set name=”set集合名称” cascade=”save-update”>
示例代码:
一个公司有多个联系人, 一个联系人只属于一个公司
实现方式一:没有配置映射文件cascade=”save-update”
Company comp = new Company();
comp.setCname(“百度”);
comp.setCphone(“13314445555”);
LinkedMan lman = new LinkedMan();
lman.setLname(“李彦宏”);
lman.setLphone(“18845888888”);
comp.getLinkedManSet().add(lman);
lman.setCompany(comp);
session.save(comp);
session.save(lman);
实现方式二:配置完映射文件
Company comp = new Company();
comp.setCname(“百度”);
comp.setCphone(“13314445555”);
LinkedMan lman = new LinkedMan();
lman.setLname(“李彦宏”);
lman.setLphone(“18845888888”);
comp.getLinkedManSet().add(lman);
session.save(comp);
一对多修改外键操作:
Company comp =session.get(Compant.class,1); //id查询
LinkedMan lm = session.get(LinkedMan.class,1);
comp.getLinkedManSet().add(lm);
lm.setCompany(comp);
因为是持久态对象,所以不用保存,也会执行;
inverse属性:因为hibernate双向维护外键,所以会update两次外键,降低了效率,解决办法,让其中的一方(多方维护外键)不维护外键;
在一方的映射文件中添加inverse=”true” ,表示放弃关系维护;
<set name=”set集合名称” inverse=”true” cascade=”save-update”>
十四、Hibernate多对多操作
多对多映射配置
第一步 创建实体类(Role /user)
第二步 让两个实体类互相表示(在实体类中分别创建一个set集合表示多对多关系)
Role{Set<User> setUser = new HashSet<User>();}
User{Set<Role> setRole=new hashSet<Role>();}
第三步 配置映射文件
(1)基本配置()
cn.wyx.User 实体类的配置文件
<class name=”实体类全路径” table=”数据库中表名”>
<id name=”id值” colum=”表中列名与实体类属性名必须相同”>
<generator class=”native”></generator>
</id>
<property name=”username” colum=”username”></property>
……
<set cascade=”save-update” name=”setRole” table=”user-role” >第三张表的名字
<key colum=”userid”></key>当前实体类在第三张表中的外键
<many-to-many class=”cn.wyx.Role” colum=”roleid”></many-yo-many>关联的哪个实体类,以及该类在第三张表中的外键
</set>
</class>
cn.wyx.Role 实体类的配置文件
<class name=”cn.wyx.Role” table=”role”>
<id name=”rid” colum=”rid”>
<generator class=”native”></generator>
</id>
<property name=”rname” colum=”rname”></property>
……
<set name=”setUser” table=”user-role”>第三张表的名字
<key colum=”rid”></key>当前实体类在第三张表中的外键
<many-to-many class=”cn.wyx.User” colum=”uid”></many-yo-many>关联的哪个实体类,以及该类在第三张表中的外键
</set>
</class>
(2)配置多对多映射(创建第三张表)
第四步 配置核心配置文件
<mapping resource=””></mapping>
级联添加、删除(操作第三张表)
根据用户保存角色
第一步 在用户配置文件中set标签进行配置,cascade = “save-update”
第二步 代码实现
User user1 = new User();
user1.setUserName(“lucy”);
user1.setPassword(“123”);
User user2 = new User();
user2.setUserName(“lucy”);
user2.setPassword(“123”);
Role r1 = new Role();
r1.setRname(“总经理”);
Role r2 = new Role();
r2.setRname(“CEO”);
Role r3 = new Role();
r3.setRname(“技术总监”);
user1.getSetRole().add(r1);
user1.getSetRole().add(r2);
user2.getSetRole().add(r1);
user2.getSetRole().add(r3);
session.save(user1);
session.save(user2);
级联删除配置:(不参考使用)
set 标签中添加cascade=”delete”
User user=session.get(User.class,1);
session.delete(user);
维护第三张表:(重点)
1、让某个用户有某个角色
第一步:根据id查询出用户和角色
User user = session.get(User.class,1);
Role role = session.get(Role.class,1);
第二步:把角色放到用户里面
user.getSetRole().add(role);
2、让用户没有某个角色
第一步:根据id查询角色和用户
User user = session.get(User.class,1);
Role role = session.get(Role.class,1);
第二步:从setRole集合中删除这个角色
user.getSetRole().remove(role);
十五、查询
对象导航查询:
public void test(){
SessionFactory sf=null;
Session session=null;
Transaction tx=null;
try{
sf = HibernateUtils.getSessionFactory();
session = sf.openSessioin();
tx = session.beginTransaction();
Category category = session.get(Category.class,1);
Category <Book> cset = category.getSetCategory();
sysout(cset.size());
tx.commit();
}catch(Exception e){
tx.rollback();
}
}
OID查询
Category category = session.get(Category.class,1); //就是根据id查询
HQL查询
第一步:使用query对象,写hql语句;
第二步:调用query方法得到结果;
例1:查询客户表中所有数据?(查询所有)
Query query = session.createQuery(“from 实体类名称”);
List<Customer> list = query.list();
for(Customer cut : list){sysout(cut.getName()+cut.getPassword())}
例2:从customer表中查询 cid=1 cusName=”百度”的数据?(where条件查询)
hql语句格式:
from 实体类名 where 实体类属性名 =? and 实体类属性名=?;
from 实体类名 where 实体类属性名 like? and 实体类属性名 like?;(用法同上传递参数可以加%参数名%)
Query query = session.createQuery(“from Customer where cid=? and custName=?”);
query.setParameter(0,1); //参数位置从0开始,第0个参数的cid值为1;
query.setParameter(1,”百度”);
Set<Customer> set = query.llist();
例3:根据cid排序?(排序查询)
hql格式:from 类名 order by 实体类属性名 desc/asc
例4:查询所有数据,从0开始显示3行?(分页查询)
在hql中没有limit,而是用Hibernate中的方法来代替;
Query query = session.createQuery(“from Customer”);
query.setFirstResult(0);//设置开始位置
query.setMaxResult(3);//设置每页记录数
list<Customer>list=query.list();
例5:查询表中指定某一列的值?(投影查询)
hql格式:select 实体类属性名 from 实体类名
Query query = session.createQuery(“select custName from Customer”);
List<Object> list = query.lislt();
例6:查询表中指定某一列的值?(聚合函数查询)
1、常用聚合函数:count/sum/avg/max/min
hql格式:select count(*) from 实体类名
Query query = session.createQuery(“select count(*) from Customer”);
Object obj = query.uniqueResult();
//注意不能直接把obj强转成int,要先转换成Long类型,再转成int;
obj = (Long)obj.intValue();
sysout(obj);
例7:客户与联系人为例(多表查询)
1、内连接
hql格式: from 实体类名 别名 inner join 别名.实体类set集合名
Query query = session.createQuery(“from Customer c inner join c.ManLinkedSet”);
List<Customer> list = query.list(); //集合中每个元素都是一个数组
2、左外连接
hql格式:from Customer c left outer join c.LinkedManSet(返回list元素数组)
3、右外连接
hql格式:from Customer c inner join c.ManLinkedSet(没有迫切连接只能返回数组元素)
4、迫切内连接
与内连接的实现一样,区别在于返回list的每部分是对象;
hql格式: from Customer c inner join fetch c.ManLinkedSet
5、迫切左外连接
hql格式:from Customer c left outer join fetch c.LinkedManSet(返回list元素对象)
QBC查询
第一步:创建Criteria;
第二步:调用方法得到结果;
1、查询所有
Criteria criteria = session.createCriteria(Customer.class);
List<Customer>list = criteria.list();
2、条件查询
Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.eq(“类中属性名”,值));//添加条件
criteria.add(Restrictions.eq(“cid”,1));
criteria.add(Restrictions.eq(“custName”,”百度”));
List<Customer> list = criteria.list();
3、排序查询
Criteria criteria = session.createCriteria(Customer.class);
//设置对哪个属性进行排序,以及排序规则
criteria.addOrder(Order.asc(“cid”)); 升序
List<Customer> list = criteria.list();
4、分页查询
Criteria criteria = session.createCriteria(Customer.class);
criteria.setFirstResult(0);
criteria.setMaxResult(10);
List<Customer> list = criteria.list();
5、统计查询
Criteria criteria = session.createCriteria(Customer.class);
criteria.setProjection(Projections.rowCount());//设置操作
Object obj = criteria.uniqueResult();//调用方法得到结果
int count = (Long)obj.intValue();
6、离线查询
//离线:不用session也可以创建Criteria对象,以及基本操作;
//创建离线对象
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(实体类.class);
//最终执行时候才需要得到session
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List<Customer> list = criteria.list();
用处:在dao里面使用hibernate框架时候,最终需要调用session里面的方法实现功能,但是查询条件,可以写在servlet中,来传给dao,而servlet 用离线对象维护;
十六、Hibernate 检索策略
第一类 立即查询:根据id查询,调用get()方法立即去查询;
第二类 延迟查询:根据id查询,当调用load方法,不会马上发送语句查询数据,只有得到对象里面的值时候才会发送语句查询数据库;
Customer customer = session.load(Customer.class,1);
调用方法之后,不会发送语句,返回对象里面只有id值,当后面语句得到不是id值的时候才会发送语句;
例如:customer.getCustName(); 就会发送语句
延迟查询中又分两类:
(1)类级别延迟:根据id查询返回实体类对象,load方法就是类级别延迟;
(2)关联级别延迟:查询出某个客户,在查询出这个客户的联系人,查询客户所有联系人的过程是否需要延迟,这个过程称为关联级别延迟;(Hibernate默认使用)
关联级别延迟修改:
1、在映射文件中进行配置
2、在set标签上使用属性
(1)fetch:select(默认)
(2)lazy:
true 延迟(默认)
false立即
extra 最延迟(要什么值发送什么语句,效率更好,但是需要发送多次语句效率就会变低)
批量抓取:
1、查询所有的客户,返回list集合,遍历list集合,得到每个用户,得到每个用户联系人;
通常我们操作会发送大量sql语句,采用批量抓取就会优化性能;
在映射文件的set标签中进行配置
添加属性 batch-size=”10” 数值自己定,但是越大发送sql语句越少;