JAVAEE 三层结构:
- WEB层 struts2
- Service层 Spring
- dao层 Hibernate
Hibernate 概述:
- 什么是框架?
1.1 写一个程序,使用框架之后,会帮我们实现一部分功能,可以让我们少写一些代码。 - 什么是Hibernate框架?
2.1 它是用在JAVAEE三层结构中的dao层。
2.2 在dao层里面实现curd操作。
2.3 是对jdbc封装。 - ORM(Object relational Mapping)
3.1 让我们实体类和我们的关系型数据库进行一一的映射关系,使用配置文件来完成。
案例:
- 导入jar包,hibernate相关jar,最后还有jdbc的jar包。
- 创建实体类
- 一般是在实体类所在包下,创建配置文件,名字一般为,实体类名.hbm.xml。
- 在xml文件中引入约束。
- hibernate 要求有一个属性是唯一的值(主键)。
- hibernate 可以让我们不要自己去创建表了,但是不能给我去创建数据库。
- hibernate 实体类映射配置,注意coloum可以省略,默认和name一样:
- hibernate 核心配置文件:
8.1 位置必须在src下,名称必须叫hibernate.cfg.xml,在hibernate运行中只会加载核心配置文件。
- 加载核心配置文件。
- 创建session工厂
- 得到session
- 开启事务
- 执行curd
- 提交事务
- 关闭资源
整个代码:
public void addUser(){ //会默认到src下去加载hibernate.cfg.xml文件 Configuration cfg=new Configuration(); cfg.configure(); //得到session工程,且这个时候会根据映射文件创建相应的表 SessionFactory factory=cfg.buildSessionFactory(); //得到session Session session=factory.openSession(); //开启事务 Transaction transaction=session.beginTransaction(); //因为主键自增长,所以不需要自己设置 User user=new User(); user.setPwd("123"); user.setUname("zhangsan"); session.save(user); transaction.commit(); session.close(); }
重要的API:
Configuration:
1.1 用于加载配置src下的hibernate.cfg.xml核心配置文件Configuration cfg=new Configuration(); cfg.configure();
SessionFactory
2.1 创建sessionfactory对象:cfg.buildSessionFactory();
2.2 在得到sessionfactory的时候,会根据映射文件,创建相应的表,所以创建sessionfactory会非常消耗资源,所以一个项目中一般就只有一个sessionfactory对象。
2.3. 写个工具类:public class HibernateUtils { private static ThreadLocal<Session>threadLocal=newThreadLocal<Session>(); private static final Configuration cfg; private static final SessionFactory factory; static{ cfg=new Configuration(); cfg.configure(); factory=cfg.buildSessionFactory(); } public static Session getSession(){ Session session=threadLocal.get(); if(session==null){ session=factory.openSession(); threadLocal.set(session); } return session; } public static void closeSession(){ Session session=threadLocal.get(); if(session!=null){ session.close(); } threadLocal.remove(); } }
session
3.1 类似于JDBC里面的connection。
3.2 可以通过sessio执行curd操作。
3.3 添加方法(save)
3.4 删除(delete)
3.5 修改方法(update)
3.6 根据id查询(get)
3.7 单线程对象:session对象不能共用。- 事务的四个特性:
4.1 原子性:一组操作,要么都成功,要么都失败
4.2 一致性:操作前后,数据总量不会变化。
4.3 隔离性:多个事务操作同一个记录,互不影响。
4.4 持久性:最终在数据库中进行存储。
实体类的编写规则:
- 属性全部私有。
- 属性都有公有的setter和getter方法。
- 要求实体类有一个属性作为唯一值。
- 实体类尽量不使用基本类型,要使用其包装类,因为可以多一种值得表示,NULL。
主键的生成策略:
- increment: 以增量为一进行自动增长,只有当没有其他进程在对同一张表进行操作的时候才可以使用,不适合于集群环境,基本没用。
- identity:要求数据库底层支持自动增长,Oracle不支持。
- sequece:要求支持序列,mysql不支持。
- native:根据用的数据库,来自动进行选择,进行自动增长,自己设置了id也没有用。
- uuid: 生成32位的uuid的字符串的值。
对实体类简单的CURD操作:
save:
Session session=HibernateUtils.getSession(); //开启事务 Transaction transaction=session.beginTransaction(); //因为主键自增长,所以不需要自己设置 User user=new User(); user.setPwd("123"); user.setUname("xiaoli"); session.save(user); transaction.commit(); HibernateUtils.closeSession();
根据id值进行查询:
2.1 使用session的get的方法。Session session=HibernateUtils.getSession(); //开启事务 Transaction transaction=session.beginTransaction(); User user=(User) session.get(User.class,"4028668159f29e9a0159f29e9c2e0000"); transaction.commit();
修改操作(查询+修改):
Session session=HibernateUtils.getSession(); Transaction tx=session.beginTransaction(); User user=(User) session.get(User.class, "4028668159f29e9a0159f29e9c2e0000"); user.setPwd("000"); session.update(user); tx.commit(); HibernateUtils.closeSession();
删除操作:
Session session=HibernateUtils.getSession(); Transaction tx=session.beginTransaction(); User user=(User) session.get(User.class, "4028668159f29e9a0159f29e9c2e0000"); session.delete(user); tx.commit(); HibernateUtils.closeSession();
实体类对象的状态:
- 瞬时态:对象里面没有id值,和session没有关联。
- 持久态:对象里面有这个id值,和session有关联。
- 托管态:对象里面有id,但是和session没关联。
- 0ave,update,saveOrUpdate的区别:
save():方法很显然是执行保存操作的,如果是对一个新的刚new出来的对象进行保存,自然要使用这个方法了,数据库中没有这个对象。
update():如果是对一个已经存在的托管对象进行更新那么肯定是要使用update()方法了,数据中有这个对象。
saveOrUpdate():这个方法是更新或者插入,有主键就执行更新,如果没有主键就执行插入。
区别:对于一个从托管状态到瞬态的对象(对于一个从数据库中取出来又被删除的对象),这个对象本身是有主键的,但是因为被删除了,所以这个时候因为数据库中已经没有了这条记录了。不过它还有主键存在,所以这个时候不可以使用update()或者是saveOrUpdate(),因为update()方法是认为数据库中肯定有这条记录的,而saveOrUpdate的执行过程就是先查看这个对象是不是有主键,有主键那么就执行update()方法,没有主键就执行save()方法,因此结果跟调用了update()方法的效果是一样的,结果就会出错,因为这个对象已经被删除了,数据库中已经没有这条记录了,只是它还有主键而已(仅仅是存在于内存中),因此这个时候要执行的是save()方法。
hibernate一级缓存
- 什么是缓存?
1.1 数据存到数据库里面,数据库系统本来就是一个文件系统,使用流操作文件,效率并不是很高。
1.2 把数据存到内存中去,可以不使用流的方式,可以直接从内存中读取数据,从而提高读取数据。 - 什么是hibernate缓存?
2.1 在我们的hibernate框架中,他帮我们做了很多的性能的优化,其中缓存机制就是其中的一种。
3 heibernate缓存特点:
3.1 一级缓存(session缓存)
一级缓存默认是打开的,一级缓存有他使用的范围,默认是session的范围,在一级缓存中的实体类对象都是持久态的对象。
特性:持久态自动更新数据库。
3.2 二级缓存
目前二级缓存基本不用了,二级缓存默认是不打开的,需要配置,使用范围是整个项目的范围,使用redis替代二级缓存。
事务的隔离级别:
- 不考虑隔离级别将引发的问题:
1.1 脏读:读到未提交的事务
1.2 不可重复读:一个事务中两个一样的操作,读取到数据却不同。
1.3 幻读(虚读):当A事务改变了所有数据行,但是另外一个B事务又插入了新的行,导致A好像没有改变过的一样。
1.4 丢失更新:当A事务改变了更新了某行数据,但是B事务又进行更新,覆盖了A事务的更新结果。 - 隔离级别与存在问题:
2.1 未提交读:脏读,幻读,不可重复读
2.2 以提交读:幻读,不可重复读
2.3 可重复读:mysql的默认隔离级别,幻读
2.4 可串行读:无
隔离级别越高,性能会越差。
事务操作的标准代码:
public void query(){
Session session=null;
Transaction tx=null;
try{
session=HibernateUtils.getSession();
tx=session.beginTransaction();
User user=(User) session.get(User.class, "4028668159f2a1570159f2a1589b0000");
user.setPwd("wang");
tx.commit();
}catch(Exception e){
if(tx!=null)tx.rollback();
}finally{
HibernateUtils.closeSession();
}
}
hibernate线程绑定session:
- 其底层原理就是通过threadLocal
在核心配置文件中进行配置
<property name="current_session_context_class">thread</property>
调用sessionfactory的方法进行得到,且不需要自己手动关闭了,线程结束自动关闭,否则会报错。
sessionFacotory.getCurrentSession();
Query对象:
- 不需要写SQL语句,但需要写HQL(Hibernate Query Language)语句。
- HQL与SQL语句的区别:
2.1 使用SQL操作的是表和字段
2.2 使用HQL操作的是实体类和他的属性。 - 查询所有:from 实体类名称
Query 对象的使用:
public void hqlQuery(){ Session session=null; Transaction tx=null; SessionFactory factory=HibernateUtils.getSessionFactory(); try{ session=factory.getCurrentSession(); tx=session.beginTransaction(); Query query=session.createQuery("from User"); List<User>list=query.list(); for(User user:list){ System.out.println(user); } tx.commit(); }catch(Exception e){ if(tx!=null)tx.rollback(); }finally{ factory.close(); } }
Criteria对象:
public void criteriaQuery(){
Session session=null;
Transaction tx=null;
SessionFactory factory=HibernateUtils.getSessionFactory();
try{
session=factory.getCurrentSession();
tx=session.beginTransaction();
Criteria criteria=session.createCriteria(User.class);
List<User>list=criteria.list();
for(User user:list){
System.out.println(user);
}
tx.commit();
}catch(Exception e){
if(tx!=null)tx.rollback();
}finally{
factory.close();
}
}
SQLQuery:
public void sqlQuery(){
Session session=null;
Transaction tx=null;
SessionFactory factory=HibernateUtils.getSessionFactory();
try{
session=factory.getCurrentSession();
tx=session.beginTransaction();
SQLQuery sqlQuery=session.createSQLQuery("select* from t_user");
sqlQuery.addEntity(User.class);//设置返回的是对象的集合,否则是数组的集合
List<User>list=sqlQuery.list();
// List<Object[]>list=sqlQuery.list();//返回的都是数组
// for(Object[]obj:list){
// System.out.println(Arrays.toString(obj));
// }
for(User user:list){
System.out.println(user);
}
tx.commit();
}catch(Exception e){
if(tx!=null)tx.rollback();
}finally{
factory.close();
}
}
一对多配置:
- 类图:
实体类:
2.1 Customer:public class Customer { private int cid; private String cname; private String phone; private Set<LinkMan>set=new HashSet<LinkMan>(); public int getCid() { return cid; } public void setCid(int cid) { this.cid = cid; } public String getCname() { return cname; } public void setCname(String cname) { this.cname = cname; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public Set<LinkMan> getSet() { return set; } public void setSet(Set<LinkMan> set) { this.set = set; } }
2.2 LinkMan:
public class LinkMan { private int lid; private String link_name; private String link_pwd; private Customer customer; public int getLid() { return lid; } public void setLid(int lid) { this.lid = lid; } public String getLink_name() { return link_name; } public void setLink_name(String link_name) { this.link_name = link_name; } public String getLink_pwd() { return link_pwd; } public void setLink_pwd(String link_pwd) { this.link_pwd = link_pwd; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } }
映射文件配置:
3.1 Customer:
3.2 LinkMan 配置:
普通保存:
public void testOneToMany(){ Session session=null; Transaction tx=null; SessionFactory factory=HibernateUtils.getSessionFactory(); try{ session=factory.getCurrentSession(); tx=session.beginTransaction(); Customer cs=new Customer(); cs.setCname("百度"); cs.setPhone("15918622895"); LinkMan linkMan=new LinkMan(); linkMan.setLink_name("李四"); linkMan.setLink_pwd("123"); cs.getSet().add(linkMan); linkMan.setCustomer(cs); session.save(linkMan); session.save(cs); tx.commit(); }catch(Exception e){ if(tx!=null)tx.rollback(); }finally{ factory.close(); } }
5.级联保存:
5.1 需要在多的一的一方配置:cascade=”save-update”
public void testOneToMany2(){
Session session=null;
Transaction tx=null;
SessionFactory factory=HibernateUtils.getSessionFactory();
try{
session=factory.getCurrentSession();
tx=session.beginTransaction();
Customer cs=(Customer) session.get(Customer.class, 3);
LinkMan linkMan=new LinkMan();
cs.getSet().add(linkMan);
session.save(cs);
tx.commit();
}catch(Exception e){
if(tx!=null)tx.rollback();
}finally{
factory.close();
}
}
6. 级连删除:
6.1 cascade=”save-updatem,delete”
public void testOneToDelete(){
Session session=null;
Transaction tx=null;
SessionFactory factory=HibernateUtils.getSessionFactory();
try{
session=factory.getCurrentSession();
tx=session.beginTransaction();
Customer cs=(Customer) session.get(Customer.class, 3);
session.delete(cs);
tx.commit();
}catch(Exception e){
if(tx!=null)tx.rollback();
}finally{
factory.close();
}
}
7: inverse属性:
因为在hibernate中,外键是双向维护的,性能上会有所影响,下面是解决方法:
7.1 可以让一的一方放弃对外键的维护:
7.2 采用配置inverse属性,配置为true
多对多配置
- 类图:
- 因为是多对多,我们可以引入第三张表
Student
3.1 Student类:public class Student { private Integer sid; private String sname; private String hobby; private Set<Teacher>set=new HashSet<Teacher>(); public Integer getSid() { return sid; } public void setSid(Integer sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby = hobby; } public Set<Teacher> getSet() { return set; } public void setSet(Set<Teacher> set) { this.set = set; } }
3.2 Student配置文件:
4. Teacher
4.1 Teacher类
public class Teacher {
private Integer tid;
private String tname;
private String gender;
private Set<Student>set=new HashSet<Student>();
public Integer getTid() {
return tid;
}
public void setTid(Integer tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Set<Student> getSet() {
return set;
}
public void setSet(Set<Student> set) {
this.set = set;
}
}
4.2 Teacher配置文件:
5. 这两个配置文件,会自动生成第三张表,字段为fsid和fsid,注意这两个配置文件的共同点,和对应关系。
6. 我们可以进行级联的保存,也可以进行级联的删除,方法类似于一对多配置,但是一般不进行级联的删除,因为一个老师对于多个学生,如果把老师去掉,该学生的信息也将不见了,所以一般通过维护第三张表来实现。
7. 维护第三张表
维护第三张表其实就是维护set集合,假设A老师没有B这个学生,直接在A老师的set集合把B学生移除就好了。
hibernate查询:
对象导航查询
1.1 查询某个客户,再查询这个客户对应的所有联系人。public void navigatorQuery(){ SessionFactory factory=null; Session session=null; Transaction tx=null; try{ factory=HibernateUtils.getSessionFactory(); session=factory.getCurrentSession(); tx=session.beginTransaction(); Customer cs=(Customer) session.get(Customer.class,5); Set<LinkMan>set=cs.getSet(); for(LinkMan temp:set){ System.out.println(temp); } tx.commit(); }catch(Exception e){ if(tx!=null) tx.rollback(); }finally{ if(session!=null)session.close(); if(factory!=null)factory.close(); } }
- OID查询
2.1 根据id查询某条记录 hql查询
3.1 Query 对象查询,与sql很相似,但是不同的地方是HQL使用的是实体类和属性,而我们的sql使用的是表名与字段。
3.2 各种查询方法:public void query2(){ SessionFactory factory=null; Session session=null; Transaction tx=null; try{ factory=HibernateUtils.getSessionFactory(); session=factory.getCurrentSession(); tx=session.beginTransaction(); // org.hibernate.Query query=session.createQuery("from Customer");//查询所有 List<Customer>list=query.list(); // Query query=session.createQuery("from Customer where cid=?"); // query.setParameter(0, 5);//注意问号是从零开始的。 // Query query=session.createQuery("from Customer where phone like ?");//模糊查询 // query.setParameter(0, "%1%"); // Query query=session.createQuery("from Customer order by cid desc");//排序查询 // Query query=session.createQuery("from Customer");//分页查询 // query.setFirstResult(0); // query.setMaxResults(5); // Query query=session.createQuery("select cid,cname from Customer");//投影查询 // List<Object>list=query.list(); // for(Object obj:list){ // System.out.println(obj); // } Query query=session.createQuery("select count(*) from Customer");//聚集函数查询 Object obj=query.uniqueResult(); System.out.println(obj); tx.commit(); // 并列条件可以用and 进行连接 }catch(Exception e){ if(tx!=null) tx.rollback(); }finally{ if(factory!=null)factory.close(); } }
3.3 HQL内连接查询:
内连接只是查询满足条件的数据// Query query=session.createQuery("from Customer a inner join a.set");//普通内连接,返回数组,迫切内连接返回对象。
3.4 外连接:
左外连接,迫切左外连接,右外连接,迫切右外连接和内连接,迫切内连接类似。QBC查询
4.1 使用Criteria对象查询
4.2 查询所有记录public void queryAll(){ SessionFactory factory=HibernateUtils.getSessionFactory(); Session session=null; Transaction tx=null; try{ session=factory.getCurrentSession(); tx=session.beginTransaction(); Criteria criteria=session.createCriteria(Customer.class); List<Customer>list=criteria.list(); for(Customer cs:list){ System.out.println(cs); } tx.commit(); }catch(Exception e){ tx.rollback(); }finally{ factory.close(); } }
4.3 条件查询:
public void querytwo(){ SessionFactory factory=HibernateUtils.getSessionFactory(); Session session=null; Transaction tx=null; try{ session=factory.getCurrentSession(); tx=session.beginTransaction(); Criteria criteria=session.createCriteria(Customer.class); criteria.add(Restrictions.eq("cid", 5)); Object obj=criteria.uniqueResult(); System.out.println(obj); tx.commit(); }catch(Exception e){ tx.rollback(); }finally{ factory.close(); } }
4.4 模糊查询:
public void querytwo(){ SessionFactory factory=HibernateUtils.getSessionFactory(); Session session=null; Transaction tx=null; try{ session=factory.getCurrentSession(); tx=session.beginTransaction(); Criteria criteria=session.createCriteria(Customer.class); criteria.add(Restrictions.like("phone", "%1%")); List<Customer>list=criteria.list(); for(Customer cs:list){ System.out.println(cs); } tx.commit(); }catch(Exception e){ tx.rollback(); }finally{ factory.close(); } }
4.5 排序:
public void orderBy(){ SessionFactory factory=HibernateUtils.getSessionFactory(); Session session=null; Transaction tx=null; try{ session=factory.getCurrentSession(); tx=session.beginTransaction(); Criteria criteria=session.createCriteria(Customer.class); criteria.addOrder(Order.desc("cid")); List<Customer>list=criteria.list(); for(Customer cs:list){ System.out.println(cs); } tx.commit(); }catch(Exception e){ tx.rollback(); }finally{ factory.close(); } }
4.6 分页查询:
public void page(){ SessionFactory factory=HibernateUtils.getSessionFactory(); Session session=null; Transaction tx=null; try{ session=factory.getCurrentSession(); tx=session.beginTransaction(); Criteria criteria=session.createCriteria(Customer.class); criteria.setFirstResult(0); criteria.setMaxResults(3); List<Customer>list=criteria.list(); for(Customer cs:list){ System.out.println(cs); } tx.commit(); }catch(Exception e){ tx.rollback(); }finally{ factory.close(); } }
4.7 统计查询:
public void rows(){ SessionFactory factory=HibernateUtils.getSessionFactory(); Session session=null; Transaction tx=null; try{ session=factory.getCurrentSession(); tx=session.beginTransaction(); Criteria criteria=session.createCriteria(Customer.class); criteria.setProjection(Projections.rowCount()); Object obj=criteria.uniqueResult(); System.out.println(obj); tx.commit(); }catch(Exception e){ tx.rollback(); }finally{ factory.close(); } }
4.8 离线查询:
public void detachedQuery(){ SessionFactory factory=HibernateUtils.getSessionFactory(); Session session=null; Transaction tx=null; try{ session=factory.getCurrentSession(); tx=session.beginTransaction(); DetachedCriteria detachedCriteria=DetachedCriteria.forClass(Customer.class); Criteria criteria=detachedCriteria.getExecutableCriteria(session); criteria.list(); tx.commit(); }catch(Exception e){ tx.rollback(); }finally{ factory.close(); } }
应用场景:方便我们将一些条件直接从其他层传到dao层。
- 本地sql查询
5.1 使用普通SQLquery进行查询
检索策略:
hibernate 检索策略分为两类,一类是立即查询,一类是延迟查询。
1. 立即查询:
一调用get方法,马上就会发送sql语句,查询数据库。
Customer cs=(Customer) session.get(Customer.class, 5);
System.out.println(cs.getCid());
System.out.println(cs.getCname());
2. 延迟查询:
一调用load方法,并不会马上发送sql,只有当需要获取对象尚不存在的
数据时才会查询数据库。
Customer cs=(Customer) session.load(Customer.class, 5);
System.out.println(cs.getCid());
System.out.println(cs.getCname());
3. 延迟查询分为两种:
类级别延迟:使用load方法根据id来进行查询,返回实体类对象,不会马上发送sql语句。
关联级别延迟:例如查询某个客户,再查询这个客户对应的所有联系人,也就是查询联系人这个过程是否需要延迟,可以在set上面进行配置。
还有批量抓取
获取List集合,再遍历list集合里面的每个Customer,得到Set集合
List<Customer> list=session.createQuery("from Customer").list();
for(Customer cs:list){
Set<LinkMan>set=cs.getSet();
System.out.println(set.size());
}
默然情况下,每次获得size都会发送一次sql语句,但是我们在Customer的配置文件中,配置batch-size,这样可能就只会发送一次了,batch size的值为整数值,越大越好,这样大大的提高了性能。