required里面的jar包,jpa包,日志包
hibernate在自行配制数据库表的时候,注意定义的方言要和对应的数据库版本一致
实体类的映射文件(ORM):(.hbm.xml)实体类和数据库表的映射以及字段之间的映射和主键的生成册略等
hibernatemapping ---class (name table) ----property(name colum)/id(name colum---generator(class(native)))
核心配置文件,文件名hibernate.cfg.xml放在src下面,
根标签configuration,内容写在sessionfactory中,
数据库文件的property,name以及value;
hibernate的基础信息的配置(sql显示和格式化,方言设定,自动创建表的ddl语言(update等));
映射文集那的引入(mapping resource)
核心API:
Configuration:有configure()方法,加载src下的核心配置文件
SessionFactory:重点,使用configuration的对象创建buildSessionFactory对象。
创建过程中,根据配置文件中的数据库配置,到数据库里面根据映射
关系创建表,但是要在核心配置文件里面加上ddl配置一般值update
但是创建表,此过程比较耗费资源,
一般来说一个项目创建一个sessionFactory对象,实现方式,写一个工具类,使用静态代码块实行一次即可。建议一个项目创建一个sessionFactory对象
Session :重点:类似于jdbc中的connection,通过sessionFactory的openSession打开
可以调用sesion里面的不同的方法,实现crud操作
添加:save,saveOrUpdate,update
修改:update
删除:delete
根据id查询:get,load
数据库操作对象:createQuery(),createSQLQuery()
条件查询:createCriteria
session对象单线程对象
所以不能其他共用,只能用自己的
Transaction:
事务,通过session的beginTransaction()进行开启
操作:提交commit
回滚:rollback();
事务的特性:acid:原子性(全部一体,要么都执行要么都不执行),
一致性(前后数据一致),
隔离性(数据之间不影响),
持久性 (事务提交,数据库生效)
实体类(持久化类)编写的规则:
实体类里面的属性要是私有的
私有属性要使用公开的get读和set写方法进行操作
要求实体类里面有一个唯一的属性作为唯一值,一般都是用id
实体类里面的属性不适用基本数据类型,建议使用包装类(因为对应内容的初始化以及一些操作都会方便很多,特别是0和null的作用;除了int为Integer;char和Character其余的都是大写)
hibernate的主键生成策略:
要求实体类重点的属性作为唯一值,主键可以不同的生成策略
生成策略有很多值
主要记住:native和uuid
increment:增量为1
identity:数据库支持自动增长:
sequence:支持序列,mysql不支持
native:根据所用的数据库,选择不同的值
uuid:使用了UUID算法来生程标识符,自动生成一个唯一的32为的16进制字符串,作为
主键,一般用于代理主键
使用uuid生成策略,实体类中的id必须是一个String字符串类型
关于实体类的操作CRUD (核心概念:状态转换加上操作)
对实体类的crud
添加:session.save(xxxx);
User user = new User();
user.setUid(1);//设置id是没有效果的
user.setUsername("zhangsan");
user.setPassword("1234");
user.setAddress("japan");
session.save(user);
根据id查询:session.get() session.get(User.class,1);
修改:(三部)session.Update()
先根据id查询,然后再进行修改。
User user = session.get(User.class,1);
user.setUsername("懂法");//状态转换,将数据库的对象架设到了实体类中
//先找到uid,根据uid进行修改
session.update(user);
//修改也可以定义类再放入参数,调用修改,但是,如果属性值没有设定的话
//那么其余字段会变成空,所以一般先查再改
删除:调用session.delete();
//第一种方式,建议使用,先查再删除(将数据库的对象架空到后台进行删除然后同步到数据库)
// User user = session.get(User.class,1);
// session.delete(user);
//第二种方法:向数据库传入参数进行删除
User user = new User();
user.setUid(3);
session.delete(user);
重点:实体类对象的状态(概念)
三种状态
瞬时态:对象里面没有id值,对象和session没有关联。(一般都save做新添加的操作)
游离态:对象里面有id值,对象和session有关联。(一般都是查出来的)
托管态:对象里面有id值,对象和session没有关联。(比如通过传入参数含id的改删方式)
常见的一个操作实体类的方法
saveOrUpdate();//即可添加也可修改
//实体类是瞬时状态的时候,该方法做的是添加操作
User user = new User();
user.setUsername("jack");
user.setPassword("520");
user.setAddress("朝鲜");
session.saveOrUpdate(user);
//实体类是托管态状态的时候,该方法做的是修改操作
User user = new User();
user.setUid(5);
user.setUsername("jackson");
user.setPassword("520");
user.setAddress("阿尔巴尼亚");
session.saveOrUpdate(user);
//实体类是持久态状态的时候,该方法做的是修改
User user = session.get(User.class,5);
user.setUsername("jace");
session.saveOrUpdate(user);
缓存:数据一般存在数据,数据库是文件系统,操作的时候一般会用流的方式,效率慢
把数据存到内存中不需要使用流的方式,可以直接读取内存中的数据
把数据放到内存中,提高读取的效率
hibernate的一级缓存
hibernate框架提供很多优化的方式
hibernate中的缓存就是一种优化方式
hibernate的缓存分为:一级,二级。
特点:
hibernate的一级缓存是默认打开的,
该一级缓存有一定的使用范围,是session的范围(session的创建到关闭)
一级缓存中存取的数据必须是持久态的数据
二级缓存已经不用的,用替代技术redis
二级缓存打开需要打开,使用范围使整个项目,sessionFactory
一级缓存的执行过程:先查询一级缓存,没有则查询数据库,返回持久态的数据
将该持久态的数据放入到一级缓存中(存入的方式:非整个对象,而是将整个对象存进去,然后给定一个空间,取一个名字;判断的时候通过id值判断进行数据判断,将零散的值组成一个新的对象进行返回);在查询第二次,先查一级缓存,如果一级缓存中有相同的数据,直接在一级缓存取出;
一级缓存的特性:
1.持久态会自动更新数据库。
User user = session.get(User.class,4);
user.setUsername("tanweiwei");
//无需调用update,当commit之后自动更新数据到数据库
tx.commit();
该特性执行原理:
创建了session之后会分为两个区
一级缓存区:将查询出来的对象放入到缓存区
快照区(副本):将查询出来的对象放入到对应快照区
当修改了持久态的时候,会修改一级缓存区的内容,不会修改一级缓存的快照区的内容。
最后提交事务的时候,将一级缓存区和快照区的内容进行比较,如果不同,那么会更新到数据库。
hibernate关于事务的操作:
1。事务:事务是一组操作要么都成功
事务的特性
不考虑隔离性:产生:脏读,不可重复读,虚读
设置事务的隔离级别,mysql默认的隔离级别:repeatable read
hibernate的事务隔离级别:
1表示read uncommited isolation
2表示read comminted isolation
4. repeatable read isolation
8.Serializable isolation
事务的规范写法:
结构
try{
开启事务
提交事务
}cacth(){
回滚事务
}finally{
关闭
}
SessionFactory sessionFactory = null;
Session session = null;
Transaction tx = null;
try {
sessionFactory = HibernateUtils.getSessionFactory();
session = sessionFactory.openSession();
//开启
tx= session.beginTransaction();
User user = session.get(User.class,4);
user.setUsername("he");
session.update(user);
//设定一个异常
int a = 10/0;
//提交
tx.commit();
} catch (Exception e) {
e.printStackTrace();
//回滚
tx.rollback();
} finally {
session.close();
sessionFactory.close();
}
hibernate也可以设置事务的隔离级别,在property中设定
<property name="hibernate.connection.isolation">4</property>
hibernate绑定session。
保证session的唯一性;
1.session类似与jdbc的connection,之前使用线程的threadLocal
2.框架帮助与本地线程绑定session,底层已经实现,按照结构配置即可
3.获取本地线程session
(1)在hibernate核心配置文件中配置
<!--配置线程锁好的session-->
<property name="hibernate.current_session_context_class">thread</property>
(2)调用sessionFactory的方法得到
//得到的是与本地线程绑定的session
return sessionFactory.getCurrentSession();
此时无需手工关闭session了因为会随着线程技术而关闭,如果关闭的话可能会有异常
额外的API
查询:
Query对象查询所有记录
不需要sql,但是需要HQL(hibernate query language)与普通sql相似
区别:普通的sql操作的是数据库的表和字段
使用hql操作的是实体类和属性:
查询所有的hql语句
Query 实体类名
使用:创建Query对象,调用query对象里面的方法得到结果
Query query = session.createQuery("from User");//括号里面是hql(from 实体类名字)
List<User> list = query.list();
for (User user:list){
System.out.println(user);
}
Criteria对象
也能进行查询,但是使用这个方法使用这个对象的时候,不需要写sql语句,直接调用方法实现。
实习过程:
创建对象
调用方法得到结果
Criteria criteria = session.createCriteria(User.class);
List<User> list = criteria.list();
for (User user:list){
System.out.println(user);
}
SQLQuery对象
使用hibernate的时候,也可以调用底层sql实现
过程与前两种相似
SQLQuery sqlQuery = session.createSQLQuery("select * from t_user");
//返回的时候,默认里面每部分是数组结构,要用数组接收
List<Object[]> list = sqlQuery.list();
for (Object[] objects:list){
System.out.println(Arrays.toString(objects));
}
优化,返回的时候每部分是对象的方式
SQLQuery sqlQuery = session.createSQLQuery("select * from t_user");
//优化,返回的list中每部分是对象的形式
sqlQuery.addEntity(User.class);
List<User> list = sqlQuery.list();
for (User user:list){
System.out.println(user);
}
表与表之间的关系
一对多:分类和商品的关系,一个分类里面有多个商品,一个商品只能属于一个分类
客户和联系人
客户:与公司有业务往来的,一
联系人:公司里面的员工,多
公司和公司员工的关系
通过外键建立关系。(建表的时候,建表原则)
多对多:订单和商品,
用户和角色
多对多需要创建第三张表,对应关系,该表中至少有两个字段作为外键,分别指向两个表的主键。
一对一:夫妻制
一对多的操作:
映射配置:
创建实体类:客户,联系人
实体类之间互相表示
在客户实体类里面表示多个联系人
一个客户里面有多个联系人
//在一个客户里面表示多个联系人,一个客户有多个联系人
//hibernate要求使用集合表示多的数据,必须使用set集合
private Set<LinkMan> setLinkMan = new HashSet<LinkMan>();
public Set<LinkMan> getSetLinkMan() {
return setLinkMan;
}
public void setSetLinkMan(Set<LinkMan> setLinkMan) {
this.setLinkMan = setLinkMan;
}
在联系人实体类里面表示所属客户
一个联系人只能属于一个客户
private Customer customer;
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
配置映射关系,一个实体类对应一个映射文件
在客户文件中要表示所有联系人
<!--表示所属联系人-->
<set name="setLinkMan">
<!--一对多建表,有外键
hibernate机制,双向维护外键,都要配置外键
column是外键名称可自定义
-->
<key column="clid">
</key>
<one-to-many class="cn.it.entity.LinkMan"></one-to-many>
</set>
在联系人映射文件中要表示所属的客户
<!--表示联系人所属客户-->
<many-to-one name="customer" class="cn.it.entity.Customer" column="clid"></many-to-one>
创建核心配置文件,引入ORM映射文件
级联操作:
级联操做主要有:
级联保存:添加一个客户,为这个客户添加多个联系人
复杂方法: //添加一个客户,为这个客户添加一个联系人
//操作
//1.创建客户和联系人
Customer customer = new Customer();
customer.setCustName("传智");
customer.setCustLevel("vip");
customer.setCustSource("网络");
customer.setCustPhone("110");
customer.setCustMobile("999");
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("lucy");
linkMan.setLkm_gender("男");
linkMan.setLkm_phone("911");
//2.在客户表示联系人,在联系人白哦是客户
//建立客户对象和联系人对象关系
//把联系人对象放到客户对象的set里
customer.getSetLinkMan().add(linkMan);
//把客户对象放到联系人里面
linkMan.setCustomer(customer);
//保存到数据库
session.save(customer);
session.save(linkMan);
简单方法:
一般根据客户添加联系人,修改一下映射文件
客户映射文件中:set里面加上一个属性:cascade="save-update"
创建客户和对象,只需要把联系人放到客户里面就可以了,最终只需要保存客户就可以了。
//添加一个客户,为这个客户添加一个联系人
//操作
//1.创建客户和联系人
Customer customer = new Customer();
customer.setCustName("百度");
customer.setCustLevel("普通客户");
customer.setCustSource("网络");
customer.setCustPhone("110");
customer.setCustMobile("999");
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("小宏");
linkMan.setLkm_gender("男");
linkMan.setLkm_phone("911");
//2.把联系人放到客户里面就ok
customer.getSetLinkMan().add(linkMan);
//3.保存到数据库
session.save(customer);
级联删除:
删除客户,以及客户的联系人。
1.在客户的映射文件中 set标签中配置cascde加上 ,delete
先根据id查出再删除
//1.根据id查询对象
Customer customer = session.get(Customer.class,2);
//2.调用方法删除
session.delete(customer);
执行的过程5步,先查客户再查联系人,再置空外键,再删除联系人,删除客户
一对多的修改的操所:
让联系人改客户
利用一级缓存的同步更新,但是新能会变差(hibernate会有双向维护外键,修改两次外键,解决方案,让其中一方放弃外键维护,一般是非外键持有方,即一的一方)
//1.根据id查出对应联系人,根据id查客户
Customer baidu = session.get(Customer.class,2);
LinkMan lucy = session.get(LinkMan.class,1);
//2.设置持久态对象的值
//把联系人放到客户里
baidu.getSetLinkMan().add(lucy);
//把客户放到联系人里面
lucy.setCustomer(baidu);
inverse属性 优化:实现外键维护放弃,配置映射文件,在set上加上inverse属性,默认值为false,改成true,提高性能
多对多的操作:
多对多映射配置
用户和角色
一。创建实体类:用户,角色,
二。让两个实体类之间互相表示
一个用户里表示所有角色set
//一个用户有多个角色
private Set<Role> setRole = new HashSet<Role>();
public Set<Role> getSetRole() {
return setRole;
}
public void setSetRole(Set<Role> setRole) {
this.setRole = setRole;
}
一个角色里表示所哟有用户set
//一个角色中有多个用户
private Set<User> setUser = new HashSet<User>();
public Set<User> getSetUser() {
return setUser;
}
public void setSetUser(Set<User> setUser) {
this.setUser = setUser;
}
三。配置映射文件关系
基本配置
配置多对多关系
userxml:
<!--table表示第三张表的名称-->
<set name="setRole" table="user_role" >
<!--key表示当前映射文件在第三张表外键名称-->
<key column="userid"></key>
<!--class:角色实体类全路径
column:角色在第三张表外键的名称(双向维护)
-->
<many-to-many class="cn.it.manytomany.Role" column="roleid"></many-to-many>
</set>
rolexml:
<set name="setUser" table="user_role" >
<key column="roleid"></key>
<many-to-many class="cn.it.manytomany.User" column="userid"></many-to-many>
</set>
四。核心配置文件引入映射
多对多级联保存(用的不多)
根据用户保存角色
配置,cascade,代码实现即可
User user1 = new User();
user1.setUser_name("lucy");
user1.setUser_password("123");
User user2 = new User();
user2.setUser_name("mary");
user2.setUser_password("456");
Role r1 = new Role();
r1.setRole_name("总经理");
r1.setRole_memo("总经理");
Role r2 = new Role();
r2.setRole_name("秘书");
r2.setRole_memo("秘书");
Role r3 = new Role();
r3.setRole_name("保安");
r3.setRole_memo("保安");
//2.建立关系,把角色放到用户里面区
//user1---r1/r2
user1.getSetRole().add(r1);
user1.getSetRole().add(r2);
//user1---r2/r3
user2.getSetRole().add(r2);
user2.getSetRole().add(r3);
//3.保存用户
session.save(user1);
session.save(user2);
多对多级联删除(了解)
在set标签上面添加cascade的delete
但是删除的时候,会影响其他的多对多
多对多维护第三张表的关系
1.通过第三章表维护关系
2.让某个用户有某个角色
根据id查询用户和角色
把角色对象放到用户set
//让lucy有经济人角色
//查询lucy和经济人
User lucy = session.get(User.class, 1);
Role role = session.get(Role.class, 1);
//把角色放到用户的set
lucy.getSetRole().add(role);
3.让某个用户没有某个角色
根据id查询用户和角色
从set集合里面把角色移除
//让某用户没有角色
//查询lucy和经济人
User user = session.get(User.class, 2);
Role role = session.get(Role.class, 3);
//从用户里面把角色去掉
user.getSetRole().remove(role);
hibernate查询的方式
1.对象导航查询
根据id查询出某个客户,再查询这个客户里面所有的联系人就可以通过对象
2.OID查询
根据id查询某一条记录,返回对象
3.hql查询
Query对象,写hql
4.QBC查询
criteria对象
5.本地sql查询
SQLQuery普通sql查询
1.对象导航查询
根据id查询出某个客户,再查询这个客户里面所有的联系人就可以通过对象(如一对多的查询)
//查询cid为1的客户,再查询客户里里面所有联系人
Customer customer = session.get(Customer.class,1);
//再查询这个客户里面所有联系人
//直接得到客户里面联系人的set集合
Set<LinkMan> linkMEN = customer.getSetLinkMan();
System.out.println(linkMEN.size());
2.OID查询
根据id查询某一条记录,返回对象
Customer customer = session.get(Customer.class,1);
3.hql查询
Query对象,写hql
创建Query对象,写hql语句,调用query对象里面的方法得到结果
常用的hql语句
查询所有:from 实体类名称
Query query = session.createQuery("from Customer");
List<Customer> list = query.list();
for (Customer customer:list){
System.out.println(customer.getCid());
}
条件查询:from 实体类名 where 实体类属性名称=?and 实体类属性名称=?
from 实体类名 where 实体类属性名称 like ?
Query query = session.createQuery("from Customer NN where NN.cid=? and NN.custName=?");
query.setParameter(0,1);
query.setParameter(1,"传智");
List<Customer> list = query.list();
for (Customer customer:list){
System.out.println(customer.getCid());
}
注意:hibernate 4.1之后对于HQL中查询参数的占位符做了改进,?改为?0,?1等
Query query = session.createQuery("from Customer NN where NN.custName like ?0");
query.setParameter(0,"%传%");
List<Customer> list = query.list();
for (Customer customer:list){
System.out.println(customer.getCid());
}
排序查询:from 实体类名 order by 实体类属性名称 asc/desc
Query query = session.createQuery("from Customer order by cid desc");
List<Customer> list = query.list();
for (Customer customer:list){
System.out.println(customer.getCid());
}
分页查询:
在hql中不能使用limit,Query中封装了两个方法实现分页
Query query = session.createQuery("from Customer ");
//设置分页数据
query.setFirstResult(0);//开始未知
query.setMaxResults(3);//设置每页记录数
List<Customer> list = query.list();
for (Customer customer:list){
System.out.println(customer.getCid());
}
投影查询:查询部分字段的值,就称投影查询
select 实体类的属性名,实体类的属性名(不可以写*)from 实体类名
Query query = session.createQuery("select cid, custName from Customer ");
List<Object> list = query.list();
for (Object object:list){
System.out.println(object);
}
聚集函数使用:
count sum avg max min
select count(*) from 实体类名
Query query = session.createQuery("select count(*) from Customer ");
//调用方法得到结果
//query对象里面有方法,直接返回对象形式
Object obj = query.uniqueResult();
Long ll = (Long)obj;
int couts = ll.intValue();
System.out.println(obj);
System.out.println(couts);
4.QBC查询
criteria对象
使用QBC的时候也有上查询:
查询所有:
Criteria criteria =session.createCriteria(Customer.class);
List<Customer> list = criteria.list();
for (Customer customer:list){
System.out.println(customer.getCustName());
}
条件:
Criteria criteria =session.createCriteria(Customer.class);
//设置条件值
criteria.add(Restrictions.eq("cid",1));
criteria.add(Restrictions.eq("custName","传智"));
List<Customer> list = criteria.list();
for (Customer customer:list){
System.out.println(customer.getCustName());
}
criteria.add(Restrictions.like("custName","%智%"));
排序
Criteria criteria =session.createCriteria(Customer.class);
//设置排序规则
criteria.addOrder(Order.desc("cid"));
List<Customer> list = criteria.list();
for (Customer customer:list){
System.out.println(customer.getCustName());
}
分页:当前页减一乘以每页记录数
Criteria criteria =session.createCriteria(Customer.class);
//设置分页参数规则
criteria.setFirstResult(0);//起始位置
criteria.setMaxResults(3);
List<Customer> list = criteria.list();
for (Customer customer:list){
System.out.println(customer.getCustName());
}
统计
Criteria criteria =session.createCriteria(Customer.class);
//设置参数规则
criteria.setProjection(Projections.rowCount());
Object obj = criteria.uniqueResult();
System.out.println(obj);
离线:可以不用session也可以创建Criteria
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
//最终执行时才需要session
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List<Customer> list = criteria.list();
for (Customer customer:list){
System.out.println(customer.getCustName());
}
使用框架的时候,要调用session实现功能;离线对象可以抽离出来在任意未知拼接,然后传入dao执行
5.本地sql查询
SQLQuery普通sql查询
HQL多表查询:
内
内链接的写法:链接客户表和联系人
from Customer c inner join c.setLinkMan
Query query = session.createQuery("from Customer c inner join c.setLinkMan");
List list= query.list();//list里面是数组的形式
System.out.println(list.size());
左外:list里是数组
Query query = session.createQuery("from Customer c left outer join c.setLinkMan");
List list= query.list();
System.out.println(list.size());
右外
迫切内链接:
与内链接的底层都一样都是inner join;区别在于放回的list每部分是对象(HQL多了一个fetch而已)
Query query = session.createQuery("from Customer c inner join fetch c.setLinkMan");
List list= query.list();
System.out.println(list.size());
迫切左外链接:list里是对象
Query query = session.createQuery("from Customer c left outer join fetch c.setLinkMan");
List list= query.list();
System.out.println(list.size());
hibernate里面检索的策略
立即检索
根据id,用get,一调用get马上发送语句查询数据库
延迟检索
根据id,用load,调用load方法不会马上发送语句查询数据,只有得到对象里面的值的时候才会发送语句去查询数据库。
//延迟加载,不会马上zhi发送sql,返回对象里面只有id值
//得到对象里面不是id的其他值才会发送语句
Customer customer = session.load(Customer.class,1);
System.out.println(customer.getCid());
System.out.println(customer.getCustName());
延迟的分类:
类级别延迟:根据id查询返回的实体类对象,调用load不会马上发送sql
关联级别的延迟:
查某个客户,再查所有联系人
Customer customer = session.get(Customer.class,1);
//未发送语句,默认的优化方式
Set<LinkMan> linkMEN = customer.getSetLinkMan();
//发送语句
System.out.println(linkMEN.size());
修改方式:
通过映射文件中配置;客户映射文件
set
lazy
false 不延迟
true 延迟 默认
extra 极懒 已经淘汰(要啥给啥,不要不给)
fetch 值一般都是select默认
批量抓取:
查询所有客户,返回list集合,便利list集合,得到每个客户的所有联系人。
通过批量抓取优化。
//低性能,发送多条sql
Criteria criteria = session.createCriteria(Customer.class);
List<Customer> list = criteria.list();
for (Customer customer :list){
System.out.println(customer.getCustName()+":"+customer.getCid());
Set<LinkMan> setLinkMan = customer.getSetLinkMan();
for (LinkMan linkMan:setLinkMan){
System.out.println(linkMan.getLkm_id()+":"+linkMan.getLkm_name());
}
}
//优化,批量抓取
在客户的set标签里: batch-size="整数值越大效率越高"
减少语句量的发送
二级缓存用于管理对象,二级缓存实在应用程序级别的SessionFactory=======================
默认关闭的