Hibernate常见问题整理

本文详细探讨了Hibernate中的get()和load()方法的区别。get()方法会立即执行SQL查询,若数据不存在则返回null,而load()方法返回的是一个代理对象,只有在调用其属性时才会执行SQL,若此时数据不存在,则抛出ObjectNotFoundException异常。此外,还讨论了事务对两者的影响以及它们在延迟加载和缓存查询上的行为。

一、session和get()方法和load()方法的区别

执行get方法的时候会去执行sql语句去数据中查询记录并返回对,如果查不到数据则返回null。

而load方法则不会真的去发出sql语句,返回一个实体代理对象(当前这个对象可以自动转化为实体对象),但当代理对象被调用时,才会去发出sql语句去数据库中并返回对象。也就是我们所说的懒加载。如果没有数据不存在,就会抛出个org.hibernate.ObjectNotFoundException异常。

 

load先到缓存(session缓存/二级缓存)中去查,如果没有则返回一个代理对象(不马上到DB中去找),等后面使用这个代理对象操作的时候,才到DB中查询,这就是我们常说的 load在默认情况下支持延迟加载(lazy)

注意:无论是查询还是删更改,都要先开启事务

    @Test
    public void testGet() {
        Session session1 = sessionFactory.getCurrentSession();
        session1.getTransaction().begin();
        Teacher t = (Teacher)session1.get(Teacher.class, 1);
        session1.getTransaction().commit();
    }
结果:

21:21:14,927  WARN DriverManagerConnectionProviderImpl:93 - HHH000402: Using Hibernate built-in connection pool (not for production use!)
Hibernate: 
    select
        teacher0_.id as id1_3_0_,
        teacher0_.birthday as birthday2_3_0_,
        teacher0_.name as name3_3_0_,
        teacher0_.rank as rank4_3_0_,
        teacher0_.title as title5_3_0_ 
    from
        teacher teacher0_ 
    where
        teacher0_.id=?


Process finished with exit code 0

验证了执行get方法的时候执行sql语句


来看load方法的情况

    @Test
    public void testLoad() {
        Session session1 = sessionFactory.getCurrentSession();
        session1.getTransaction().begin();
        Teacher t = (Teacher)session1.load(Teacher.class, 1);
        session1.getTransaction().commit();
    }
结果如下:

21:26:47,243  WARN DriverManagerConnectionProviderImpl:93 - HHH000402: Using Hibernate built-in connection pool (not for production use!)


Process finished with exit code 0


    @Test
    public void testLoad() {
        Session session1 = sessionFactory.getCurrentSession();
        session1.getTransaction().begin();
        Teacher t = (Teacher)session1.load(Teacher.class, 1);
        session1.getTransaction().commit();
        System.out.println(t.getClass());
    }

结果:

class cn.gduf.hibernate.domain.Teacher_$$_jvste9d_0

可以看到load得到的对象是个代理对象,这个代理对象的父类是Teacher,因为它有父类的方法。这个代理对象是javassisit生成的,javassist的通过直接生成二进制文件来进行动态代理的,不经过编译,所以hibernate的动态代理是比较高效的。


没有发出sql语句

现在输出下t

    @Test
    public void testLoad() {
        Session session1 = sessionFactory.getCurrentSession();
        session1.getTransaction().begin();
        Teacher t = (Teacher)session1.load(Teacher.class, 1);
        System.out.println(t);
        session1.getTransaction().commit();
    }
结果:

21:29:49,715  WARN DriverManagerConnectionProviderImpl:93 - HHH000402: Using Hibernate built-in connection pool (not for production use!)
Hibernate: 
    select
        teacher0_.id as id1_3_0_,
        teacher0_.birthday as birthday2_3_0_,
        teacher0_.name as name3_3_0_,
        teacher0_.rank as rank4_3_0_,
        teacher0_.title as title5_3_0_ 
    from
        teacher teacher0_ 
    where
        teacher0_.id=?
Teacher{id=1, name='ff', title='dfasda', wifeName='null', birthday=2016-12-15, rank=PRIMARY}


Process finished with exit code 0

发现在调用了这个对象的时候才会去数据库中查询


如果我把事务先提交之后再来调用呢?

    @Test
    public void testLoad() {
        Session session1 = sessionFactory.getCurrentSession();
        session1.getTransaction().begin();
        Teacher t = (Teacher)session1.load(Teacher.class, 1);
        session1.getTransaction().commit();
        System.out.println(t);
    }

结果:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session


at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
at cn.gduf.hibernate.domain.Teacher_$$_jvst498_0.toString(Teacher_$$_jvst498_0.java)
at java.lang.String.valueOf(String.java:2849)
at java.io.PrintStream.println(PrintStream.java:821)
at cn.gduf.session.SessionTest.testLoad(SessionTest.java:93)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)





Process finished with exit code -

这个时候报错了,懒加载初始化错误,没有session

因为之前没有真的去数据库中查找并返回一个对象,而是返回的代理对象,现在调用这个对象的时候要去数据库中查找了,这个时候事务已经提交,session已经关闭了,所以会报错。


深入区别get方法和load方法

根本区别在于:

 

1.如果你使用load方法,hibernate认为该id对应的对象(数据库记录)在数据库中是一定存在的,所以它可以放心的使用,它可以放心的使用代理来延迟加载该对象。在用到对象中的其他属性数据时才查询数据库,但是万一数据库中不存在该记录,那没办法,只能抛异常,所说的load方法抛异常是指在使用该对象的数据时,数据库中不存在该数据时抛异常,而不是在创建这个对象时。

 

2.由于 session中的缓存对于hibernate来说是个相当廉价的资源,所以在load时会先查一下session缓存看看该id对应的对象是否存在,不存在则创建代理。所以如果你知道该id在数据库中一定有对应记录存在就可以使用load方法来实现延迟加载。

3.对于get方法,hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查数据库,数据库中没有就返回null。

 

4.get方法如果在session缓存中找到了该id 对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加载过,那么返回的还是原先的代理对象,而不是实体类对象,如果该代理对象还没有加载实体数据(就是id以外的其他属性数据),那么它会查询二级缓存或者数据库来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据。

5.get方法首先查询session缓存,没有的话查询二级缓存,最后查询数据库;反而load方法创建时首先查询session缓存,没有就创建代理,实际使用数据时才查询二级缓存和数据库。

 

总之对于get和load的根本区别,一句话,hibernate对于load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,只能抛异常;而对于get方法,hibernate一定要获取到真实的数据,否则返回null。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值