Hibernate的初级使用

hibernate我所理解的就是一种操作数据库的技术,把实体类与数据库中的字段配置成映射关系,通过操作实体类的方式完成对数据库操作的持久化。并且可以根据配置识别自动生成对应数据库语言的SQL语句,并自动执行SQL语句。相比JDBC减轻了开发者的重复性工作,并更加符合面对对象的开发思想。

一:搭建hibernate的使用环境

二:对象在hibernate中的三种状态、增(增加完成后可以返回该条数据的主键)删改查、分页查询、总记录查询

三:hibernate中的关系

四:缓存分析

五:事务、延迟加载、乐观锁(version)、C3PO连接池(现在来讲,比较古老了)、两种session的获取与区别

六:总结

一:搭建hibernate的使用环境

  1. 要使用hibernate完成对数据库的持久化操作,首先要实现以下配置:

①在eclipse的java Project项目中导入hibernate所必须的jar包;

②在src下新建hibernate.cfg.xml主配置文件----设置数据库连接、配置hibernate具备的属性、导入实体类映射文件等

<?xml version="1.0" encoding="UTF-8"?> com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/sh?characterEncoding=UTF-8 root root
<!-- hibernate配置 -->
	<property name="dialect">org.hibernate.dialect.MySQLDialect</property>		
	<property name="current_session_context_class">thread</property>		
	<property name="show_sql">true</property>		
	<property name="hbm2ddl.auto">update</property>
<!-- 映射文件 -->	
	<mapping resource="hibernate/Hero.hbm.xml"/>	
</session-factory>
此配置中声明了hibernate连接那个数据库,以及对hibernate的设置,

dialect是告诉数据库我连接的是MySQL,请在生成SQL语句或者其他操作时,按照MySQL的语法来执行;

current_session_context_class是hibernate的事务管理方式,即每个线程代表一个事务;

show_sql为true时,是配置了在控制台打印生成的SQL语句;

hbm2ddl.auto这个配置代表自动更新表结构,也就是hibernate可以自动的在数据库中创建表。无需自行创建。

mapping resource指向了实体类与数据库字段做映射的配置文件路径。

③src下新建pojo包,创建实体类Hero.java,设置name和id属性,并给出get、set方法;

④src下新建hibernate文件夹,创建Hero.hbm.xml文件

<?xml version="1.0" encoding="UTF-8"?> package指向的是该实体类所在的包,以src为根目录。

标签即代表一个映射类。在该标签属性中,name属性是对应的package包下的需要进行映射的实体类,table对象数据库中的表名。

在class标签中,必须设置,因为他声明了该表的主键对应实体的那个属性。name是属性名,column代表对应哪个字段,该配置是说该id的增长方式按照配置的数据库的增长方式来(例如数据库中没有该表,则hibernate会创建这个表,并设置id为主键,设置为自增长)

该标签是配置除主键外其他的属性与字段的映射关系,name是属性,column是字段。

⑤src下新建action包,创建Test.java文件

public class Test {
public static void main(String[] args) {
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session s = sf.openSession();
s.beginTransaction();
Hero hero = new Hero();
hero.setName(“女警”);
s.save(hero);
s.getTransaction().commit();
s.close();
sf.close();
}
}
此时,右键运行该文件,则数据库中会生成hero表,并插入了一条数据。一套完整的hibernate配置搭建就完成了并能够成功运行。此时就能够感受到,hibernate的面向对象的思想,通过操作对象的方式完成对数据的持久化。似不似很方便了。

二:增删改查

对象在hibernate中的三种状态:瞬时、持久、托管。

当对象被new创建,并设置了属性值,没有跟hibernate发生任何关系,JVM停止运行,该对象就烟消云散。此时为瞬时状态
该对象与hibernate发生了关系,并且有对象的session,在数据库中有对应的记录,且该对象存在session的缓存中。则该对象为持久化状态。
该对象在数据库中有对应的一条记录,但是session被关闭了,该对象不存在session的缓存中了。对象不能跟数据库进行通讯,它恢复成了一个普通的对象了。此时为托管状态。例如通过session的get方法获取到id为1的hero对象,然后session关闭了,hero对象虽然有个数据库查询出来的值,但它此时仅仅是一个hero对象,修改它与数据库没有任何关系了。
对于session对象来讲,他就是代表一次程序与数据库的对象,当它开启后,相当于设置了映射关系的对象就可以与数据库进行通讯,通过修改对象完成对数据库的操作。此时对象就是处于持久化的状态。当session关闭后,通讯切断。程序恢复正常。

通过hibernate完成数据库的操作过程中,大多可遵循以下顺序:

创建sessionFactory, 通过sessionFactory打卡一个session对象,该session对象开启一个事务(相对于开启一个线程)

执行数据库操作逻辑,session对象提交事务,关闭session,关闭sessionFactory。

  1. 以增加为例,如下:

public class Test {
public static void main(String[] args) {
//创建sessionFactory
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session s = sf.openSession();//通过sessionfactory打开一个session对象
s.beginTransaction();//session对象开启一个事务
Hero hero = new Hero();
hero.setName(“女警”);
s.save(hero);//执行数据库操作
s.getTransaction().commit();//session对象提交事务
s.close();//关闭session
sf.close();//关闭sessionfactory
}
}
在此过程中,当session.save方法执行之后,其实可以观察到数据已经传递到数据库那里去了,相当于在程序与数据库之间,有一个中间层,当执行save方法之后,该数据被放到了中间层,只有在session把该事物提交之后,才真正的锤落定音,持久化到了数据库中。之前我们在hibernate.cfg.xml配置文件声明了一个事务一个线程,因为事务具有线程不安全性,所以我们在使用hibernate时,要做到一个数据库操作创建一个session,使用完后关闭掉。

使用save保存一条数据可以通过int id = (Integer)s.save(hero)这种方式获取到save返回来的这条数据的主键。

  1. 删除即调用session的delete(hero)即可完成

  2. 修改调用update(hero)即可完成,这里通过get(实体类.class,id值)的方式获取id为1的对象,然后修改该对象的属性,执行update完成更新,如下:

public class Test {
public static void main(String[] args) {
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session s = sf.openSession();
s.beginTransaction();
Hero hero = new Hero();
hero = (Hero)s.get(Hero.class, 1);
hero.setName=“女警改1”;
s.update(hero);
s.getTransaction().commit();
s.close();
sf.close();
}
}
4. 查询就有点多样化了。

①首先可以通过get()或者 load()方法获取,这两种方法的区别会在下面进行介绍。

②通过hql获取,hibernate的专门用于查询的语句,不同于sql,比如hql在查询时,使用类名而不是表明,select Hero where id=1

public class Test {
public static void main(String[] args) {
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session s = sf.openSession();
s.beginTransaction();
String name = “女”;
Query q =s.createQuery(“from Hero p where p.name like ?”);
q.setString(0, “%”+name+"%");
List ps= q.list();
for (Hero p : ps) {
System.out.println(p.getName());
}
s.getTransaction().commit();
s.close();
sf.close();
}
}
即完成了对hero表中的数据进行模糊查询,并通过q.list(),返回查询到的数据集合。

③SQL,同样使用SQL进行模糊查询如下:

    String name = "女";         
    String sql = "select * from hero  where name like '%"+name+"%'";         
    Query q= s.createSQLQuery(sql);
    List<Object[]> list= q.list();
    for (Object[] os : list) {
        for (Object filed: os) {
            System.out.print(filed+"\t");
        }
        System.out.println();
    }

使用SQL查询list返回的是一个object类型的集合。

④Criteria进行查询,这种查询方式是完全面对对象的方式进行查询:

     String name = "女";          
    Criteria c= s.createCriteria(Hero.class);
    c.add(Restrictions.like("name", "%"+name+"%"));
    List<Hero> ps = c.list();
    for (Hero p : ps) {
        System.out.println(p.getName());
    }

有关更多Criteria的使用方法,可以查询hibernate的官方文档或者论坛使用手册。

  1. 对于分页查询和总记录查询:

可以事情hql查询,调用q.uniqueResult()方法获取总数。

    String name = "女";         
    Query q =s.createQuery("select count(*) from Hero  where name like ?");
    q.setString(0, "%"+name+"%");
    long total= (Long) q.uniqueResult();
    System.out.println(total);

hibernate的分页查询,hibernate使用Criteria来进行分页查询,使用十分的简单。

c.setFirstResult(2); 表示从第3条数据开始
c.setMaxResults(5); 表示一共查询5条数据

    String name = "女";       
    Criteria c= s.createCriteria(Hero.class);
    c.add(Restrictions.like("name", "%"+name+"%"));
    c.setFirstResult(2); //表示从第三条数据开始,相当于limit的第一个参数
    c.setMaxResults(5);  //显示5条数据,相当于limit的第二个参数

三:hibernate中的关系

多对一、一对多、多对多关系。

  1. 多对一:

多个Product对应一个Category ,大致理解就是,在product类中增加Category属性,在保存该product时,设置setCategory属性。此时通过配置映射文件中的多对一关系,会在product表中自动添加cid(cid的值为该Category对象的主键)作为该表的外键。

创建Product、Category类,并配置映射文件。在Product.hbm.xml文件中配置增加多对一关系。代表Product类中的category属性对应的是Category这个映射类,并对应product数据库中的cid。

将这两个映射文件添加到hibernate.cfg.xml中,在test类中,此时更新p时,p的cid即为c对象的主键id。
    Category c =new Category();
    c.setName("c1");
    s.save(c); 
    Product p = (Product) s.get(Product.class, 8);
    p.setCategory(c);
    s.update(p);
  1. 一对多

一个Category对应多个Product ,大概意思就是一个分类中有多个产品,在Category实体中增加属性set products代表这个Category对象中的product集合。在Category映射文件中配置了一对多关系后,查询一条Category对象的值,通过获取products属性,就可以获取到product表中cid的值为该Category对象主键的所有product记录。

配置文件如下:通过标签进行设置,因为我们的products属性是set类型的。name为该属性的名。lazy为是否延迟加载,有关延迟加载后面会有介绍。key代表这个表对应的外键是cid,为配置一对多关系,class为一对多的多是那个类。

在test.java 中,查询id为1的Category对象记录,并获取products属性,将该属性遍历输出,即可得到product表中所有cid为1的product记录。
    Category c = (Category) s.get(Category.class, 1);
    Set<Product> ps = c.getProducts();
    for (Product p : ps) {
        System.out.println(p.getName());
    }
  1. 多对多

①一个用户User可以购买多个Product,一个product可以被多个User用户购买,则user和product为多对多关系。

②创建user类,user类中添加set products。product中添加set users属性。

③在user.hbm.xml中:设置多对多关系,也就是相当于给products这个属性设置多对多关系。并告诉数据库,user与product的对应关系保存在user_product这张表中。

key代表这个对象在user_product表中的主键,也就是说获取一条user对象时,然后查看他买了哪些product。需要去user_product表中寻找uid为user的主键的对应的所有pid记录,即该用户对应的多个product是哪些。

many-to-many中设置了class,也就是这个products集合属性对应的哪个类,column的意思是在user_product表中创建字段pid,当插入一个user记录时,获取到user中所有product对象,把对象的Product类中映射对应的主键,插入到这个pid中。一个uid可以对应多个pid。在数据库中形成一对多关系

代表user的id为1的对象对应了id为2,3,4,5的四个product对象。

④product.hbm.xml也配置多对多关系,中,如果是查询一个product对象时,当获取users属性时,则会寻找pid为该对象id的所有uid的值,并根据这些uid寻找class中对应的映射文件,寻找所有id为这些uid的记录,查询出来放在users属性中。

当插入一条product对象时,会将users中所有的user对象的主键插入到该表的uid字段中,pid为这一个product对象的主键,就会产生这样的多条记录。一个pid对象多个uid

四:缓存分析

hibernate分为二级缓存和一级缓存。

简单点讲,一级缓存中缓存是存在session中的,会随着session的关闭而清除。例如session开启,在获取一条id为1的user之后,hibernate会去数据库进行物理查询,并将查询结果返回,生成缓存放在session中,在改事务再次查询id为1的user时,则不会进行数据库物理查询,而是直接从缓存中获取。并且该缓存只在当前session对象中有效,在其他session对象中同样无法获取。当session关闭后,缓存对应消除,再次查询需要进行物理查询数据库。

二级缓存是存在sessionFactory中的,不会随着session的关闭而清除。获取id为1的user对象时,session会进行物理数据库查询,此时返回查询结果,并将缓存存在sessionFactory中,该缓存可以直接在其他session对象中使用,就算当前session关闭后同样有效,只要sessionfactory不关闭即可。只会查询一次数据库。

  1. 如何配置二级缓存

在hibernate.cfg.xml中声明开启二级缓存,这里使用的是第三方 EhCache提供的二级缓存,因为hibernate本身不提供二级缓存。

true

org.hibernate.cache.EhCacheProvider

早src目录下,创建一个ehcache.xml用于EHCache的缓存配置

对要使用二级缓存的实体类映射文件添加二级缓存配置: 二级缓存配置
</class>

此时,hibernate就已经开启了二级缓存。

二级缓存的开启会大大减少连接数据库进行物理查询的时间,会对程序的整体性能有很大的提高。

五:事务、延迟加载、乐观锁(version)、C3PO连接池(现在来讲,比较古老了)、两种session的方式与区别

  1. 事务:对hibernate的任何操作,都要放在事务中进行,并且在事务提交时,要么都成功,要么都失败。

也就是说在一个事务中,进行两个操作,如果A操作和B操作都能正常执行,则事务提交成功,两个都生效了。

但如果A正常执行,B执行失败,则事务提交时,AB都不会生效。

一个事务的开始是由:s.beginTransaction();开始,由s.getTransaction().commit();结束。

并且MySQL的表类型是INNODB才支持事务。

  1. 延迟加载

通过get方法和load方法获取对象有什么不同呢?
通过get方法获取对象时,hibernate会直接去数据库进行查询,获取记录返回结果。

通过load方法获取时,hibernate并不会直接去查询数据库,而是返回一个代理对象,该对象所有属性均为初始值,当程序调用对象的属性时,例如调用user.getName()时,hibernate才会真正的查询数据库,返回该条记录。

也就是说,如果使用load方法查询时,hibernate就认定数据库中一定存在这条记录。所以先使用代理对象,如果数据库中没有该条记录,则会报错。get如果没有该条记录,不会报错,会返回null。

关系的延迟加载
在设置了一对多等关系的实体中,如果设置了延迟加载,即lazy=“true”,俗称懒加载。则在获取该实体对象时,例如User实体类中有一个set products属性,在获取时,hibernate并不会查询该user对应的所有product集合值,而是返回一个代理对象products,当user对象真正调用这个属性中的值时,如user.getProducts才会去进行数据库查询。

  1. 乐观锁(version)

如果有这样的情景,两个session同时获取一个id为1的product,数据库中价格为1000。s1修改价格值为+1000,s2修改name值为+1500。s1sava并提交事务,此时product的价格为2500,s2sava并提交事务,此时有覆盖了s1修改的内容成为了2000,。如何避免这种情况的发生,hibernate提供了乐观锁机制,也就是给这个记录添加一个版本号,用来控制同时修改的情况。

添加乐观锁:在实体的映射文件中,添加标签,并在Product类中添加version属性。

此时在运行上述情境中的代码就会报错,提示本对象已经被修改过了,无法进行修改。原理分析:

id为1的product的对象初始version为1,s1和s2同时获取该对象,version均为1。s1修改价格+1000,执行save,并提交事务,此时id为1的product的version会被修改为2。s2修改价格+2000,执行sava,并提交事务,提交事务是hibernate检测到id为1的这个对象的version不等于1了,已经是2了物是人非了。所以会报错,版本不一致,无法修改。则s2就修改失败了。

  1. C3PO连接池(现在来讲,比较古老了)

使用连接池技术主要是用来节省数据库连接时间的,可以让程序建立多条数据库连接。

使用C3PO连接池只需要两步即可,添加C3PO的jar包,在hibernate.cfg.xml配置中设置C3PO连接池。即可

    <property name="hibernate.connection.provider_class"> 
        org.hibernate.connection.C3P0ConnectionProvider 
    </property> 
    <property name="hibernate.c3p0.max_size">20</property> 
    <property name="hibernate.c3p0.min_size">5</property> 
    <property name="hibernate.c3p0.timeout">50000</property> 
    <property name="hibernate.c3p0.max_statements">100</property> 
    <property name="hibernate.c3p0.idle_test_period">3000</property> 
    <!-- 当连接池耗尽并接到获得连接的请求,则新增加连接的数量 -->
    <property name="hibernate.c3p0.acquire_increment">2</property> 
    <!-- 是否验证,检查连接 -->
    <property name="hibernate.c3p0.validate">false</property>   
  1. 两种session的方式与区别

hibernate提供了两种获取session的方式,一种是sf.openSession;一种是sf.sf.getCurrentSession();

两种session中,区别在于

对于事务的提交:
openSession只有在增加,删除,修改的时候需要开启提交事务,查询时不需要的

getCurrentSession是所有操作都必须放在事务中进行,并且提交事务后,session就自动关闭,不能够再进行关闭

对于获取
openSession每次都会获取一个新的session

getCurrentSession如果在同一个线程中,每次getCurrentSession获取的都是同一个session;在不同的线程中,每次getCurrentSession会获取不同的session。

六:总结

为什么hibernate会比JDBC的效率低呢?因为hibernate对JDBC进行了轻量级的封装,它相对JDBC还需要绑定对象做映射关系,从一个简单的JDBCSQL查询变成了复杂的SQL查询。举个例子,保洁阿姨去五楼捡一个矿泉水瓶,JDBC就是保洁阿姨直接走到五楼A房间,捡起瓶子。使用了hibernate的话,保洁阿姨就会调用一个机器人,让机器人去捡瓶子。原本是直接去做,现在多了命令机器人,而且还要经过算法的计算才会把瓶子捡起来,所以效率变低了,但是节省了保洁阿姨的力气,他只需要关注调用机器人就好,不用关系如何捡起瓶子。而且hibernate虽然效率低一点,但是hibernate有一个很强大的优势,那就是支持缓存,可以更大的减少连接资源、提高系统的运行效率。

hibernate的使用其实很简单,只要理解了它想表达的那种面对对象的思维通过对象操作数据库,就能基本掌握它的意思。至于用法,它就像一个工具,用法和技巧,不都是积累出来的么,每次掌握一个用法,久而久之对它就会很熟悉了。此篇文章是我自己的学习笔记,也是用来方便自己日后查阅的。分享出来有需要的朋友也可以来查阅,我会尽可能的写的详细明了。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值