JavaWeb框架-Hibernate-1-我来了,我看见,我征服!

本文详细介绍Hibernate框架的环境搭建过程,包括Maven项目配置、核心API使用方法,并通过具体示例演示了如何利用Hibernate进行数据库操作。

话说:

框架,我来了,我看见,我征服!

前面一系列的JDBC“打怪升级”,走到了今天。笔者只想说,Web框架,终于轮到总结你们啦。

今天的主角是:Hibernate

目录:


一、Maven的下载、安装、工程创建
二、Hibernate初识、开发环境搭建
三、HibernateAPI操作数据库
四、总结


一、Maven的下载、安装、工程创建

后续所有的框架都是利用Maven来进行管理。Maven最大的作用就是结构清晰、导包方便。无论项目中各种包包有多少,都不会因为包包而增加整个项目的整体“体重”。
Maven安装和工程建立这里不再赘述,网上资料很多。

欢迎参考笔者笔记:
http://note.youdao.com/noteshare?id=99b36d0e601fb56725fb7a7acece4aae&sub=D36A8FC839854BEE8C17FD6793E53BA3

笔者使用IDE时IDEA(2017.2.5),整体布局如下:

这里写图片描述

二、Hibernate初识、开发环境搭建

What Hibernate?
Why Hibernate?
How?

个人理解:Hibernate是一种框架技术,大大简化了JDBC底层代码,不会SQL照样可以实现CURD。一句话,很简单!读者如果看了前面一系列JavaWebJDBC的文章,就会明白这一路优化过来,都是为今天埋足了伏笔。

最大的特点:ORM-Oriented Relational Mapping对象关系映射
、数据瞬时状态与持久化状态之间的转换

简单说:瞬时状态就是New对象时候的状态,每个对象对应数据库中的一行数据(之前使用反射机制实现);持久化就是把这个对象永久的保存到了数据库中。以前是我们自己写SQL语句,利用Java反射机制,实现CURD,现在都成为过去式啦!框架底层写好,直接调用即可!
这里不做过多概念解释,先来实现其功能,读者在回头查阅相关资料反复琢磨体会即可。

怎么用?
首先看官网。

1、导入Hibernate core包

在pom.xml中添加依赖,导入Hibernate -core包。

<!-- hibernate jar -->
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>5.2.10.Final</version>
</dependency>

导入时候,注意观察仓库变化。

这里写图片描述

只有看到这个包才证明真正下载完毕!

这里写图片描述

查看一下,这个时候光核心包就6M ,但是我们把项目复制后,发现才3K,这样我们给别人传项目的时候,就很容易。不像以前那样一大堆.jar包,对方会根据pom.xml自动下载。这就是Maven目前的第一个好处。体会到了嘛?

2、创建hibernate.cfg.xml配置文件

配置看官网,但是不齐全,所以需要下载整个包。下载5.2.10后,在对应位置找到project这个文件夹,找到这几个配置,复制hibernate.cfg.xml到项目sources目录下,删掉多余配置。根据hebernate.properties配置数据库相关信息:

这里写图片描述

结果如下:

<session-factory>
   <!--配置方言-->
   <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
   <!--配置驱动-->
   <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
   <!--配置url-->
   <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/news_db</property>
   <!--配置用户名-->
   <property name="hibernate.connection.username">root</property>
   <!--配置用户密码-->
   <property name="hibernate.connection.password">119913</property>

</session-factory>

3 Junit测试

Junit测试好处明显。之前是通过main方法测试,一个类里面只能有一个main方法,需要不断的注释,一个一个测试。而Junit测试就很方便,加个注解即可。
可以测试多个方法,而且想测试哪个就测试哪个。

如果@Test用不了,就Alt+Enter导入包即可。

4、ORM

Object Relational Mapping 对象关系映射
1)编写对象的时候,以面向对象的方式处理数据
2)保存数据的时候却以关系型数据库的方式存储

有什么卵用?很简单,我们创建的实体类和数据库中的表之间是怎么关联起来的呢?Hibernate就用ORM搞定了这个问题。之前用JavaBean的时候,属性和数据库表的字段要保持一致,一旦不一致,就比较麻烦。But…..从此,摆脱这种束缚!

2中方法可以处理实体类和数据库表的关系:
法一:配置
法二:注解
用法一的话:在下载的hibernate包下面找到hbm.xml(搜索)文件,然后根据里面的配置。
注解简单,就用注解法,只需三步走:

1)在实体类News中加注解

package com.hmc.hibernate.model;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;

/**
 * User:Meice
 * 2017/10/24
 */
@Entity
@Table(name = "t_news")
public class News {
    private int id;
    private String title;
    private String author;
    private String pic;
    public News() {}

    public News(int id, String title, String author, String pic) {
        this.id = id;
        this.title = title;
        this.author = author;
        this.pic = pic;
    }

    public News(String title, String author, String pic) {
        this.title = title;
        this.author = author;
        this.pic = pic;
    }

    public News(String title, String author) {
        this.title = title;
        this.author = author;
    }


    @Id
    @GenericGenerator(name = "myid",strategy = "increment")
    @GeneratedValue(generator = "myid")
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }


    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }

    @Override
    public String toString() {
        return "News{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", pic='" + pic + '\'' +
                '}';
    }
}

注解详细含义如下:

给类加注解


@Entity
@Table(name = “t_news”)
public class News {}


@Entity代表这个类是实体类
@Table表示选择哪个表

给字段加注解


@Id
@GenericGenerator(name = “myid”,strategy = “increment”)
@GeneratedValue(generator = “myid”)
public int getId() {
return id;
}


为什么在get方法上面添加注释?因为官方文档丫!而且,get一般是我们自己根据条件来限制和操作属性的。
其中,@ID表明是主键了;@GenericGenerator是主键生成策略
如果表字段与属性不一致,就给属性加column注解


@Column(name = )


要知道,之前我们做Java反射的时候,类的属性名要和字段名一模一样,对吧?如果不一样,那么反射的时候就找不到了,现在就摆脱了这个限制,数据库表的字段随意定义,加载一个注解即可。

补充:
主键生成策略:
native
increment
uuid
更多的主键生成策略参考:
http://blog.youkuaiyun.com/wanghuan203/article/details/7562395

2)配置实体类的映射

在hibernate.cfg.xml中增加如下配置

<!--配置关联映射-->
   <mapping class="com.hmc.hibernate.model.News"></mapping>

这样,注解就正式生效啦!

3)测试

package com.hmc.hibernate;
import com.hmc.hibernate.model.News;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.Test;
import java.util.List;

/**
 * User:Meice
 * 2017/10/24
 */
public class HibernateTest02 {
    @Test
    public void test() {
        //注册服务
        final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
                .configure() // configures settings from hibernate.cfg.xml
                .build();
        //获取sessionFactory
       SessionFactory sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
        //获取session
        Session session = sessionFactory.openSession();
        //查询t_news表中的数据
        List<News> list = session.createQuery(" from News").list();//等价于select * from t_news;
        System.out.println("总条数:"+list.size());

        //关闭session
        session.close();

    }
}

这里的测试都是参考官方文档,不是空穴来风奥。以上代码很少,核心是得到session,怎么得到session?通过sessionFactory,如何得到sessionFactory?通过registry。固定的模式,类似JDBC连接一样。后续我们把这个封装起来。

5、自动创建数据表

前面测试的基础是数据库有这张表,如果数据库没有这个表呢?是否可以实现自动生成,木问题!

只要先创建实体类,做同样的注解==》配置实体映射和自动生成属性==》运行一个测试(这个测试随便哪个都行,但是就是要先创建工厂,然后创建session之类的)

比如:我们创建一个新闻评论实体类如下,此时数据库无此表。

package com.hmc.hibernate.model;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;

/**
 * User:Meice
 * 2017/10/24
 */
@Entity
@Table(name = "t_comment")
public class Comment {
    private int id;
    private String name;
    private String content;

    @Id
    @GenericGenerator(name = "myid",strategy = "increment")
    @GeneratedValue(generator = "myid")
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Column(name = "com_name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

做如下配置:

<!--配置DDL-->
<property name="hibernate.hbm2ddl.auto">update</property>

<!--配置关联映射-->
<mapping class="com.hmc.hibernate.model.News"></mapping>
<mapping class="com.hmc.hibernate.model.Comment"></mapping>

这个DDL配置是参考包里面的project下面的hiberante.properties的。测试的时候,直接运行HibernateTest02即可。于是,comment表就在MySQL数据库中自动创建了。

hiberante.properties的Miscellaneous Settings模块里,有这样几个配置:
hibernate.hbm2ddl.auto create-drop——先删除后在创建
hibernate.hbm2ddl.auto create——直接创建
hibernate.hbm2ddl.auto update——如果有,就更新;没有就创建

6、自动打印SQL语句

你知道么?Hibernate可以查看底层的SQL语句咧!

我们先删掉之前自动生成的表comment;在执行测试HibernateTest02;结果就会显示创建表的语句及查询语句
在hibernate.cfg.xml中增加如下配置:

<!--打印SQL语句-->
<property name="hibernate.show_sql">true</property>

打印输出结果:

INFO: HHH000262: Table not found: t_comment
十月 24, 2017 9:37:56 上午 org.hibernate.tool.schema.extract.internal.InformationExtractorJdbcDatabaseMetaDataImpl processGetTableResults
INFO: HHH000262: Table not found: t_comment
Hibernate: create table t_comment (id integer not null, content varchar(255), com_name varchar(255), primary key (id))
十月 24, 2017 9:37:56 上午 org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select news0_.id as id1_1_, news0_.author as author2_1_, news0_.pic as pic3_1_, news0_.title as title4_1_ from t_news news0_
总条数:509

这里打印输出的语句虽然和我们写的有点区别,但是粘贴后可以执行的。

结论:
1、Hibernate底层跟我们之前写的一样,还是写的SQL,而且还改变了下SQL,所以底层执行效率低;
2、难度由代码转化到配置上,配置简单。

三、HibernateAPI操作数据库

1、未简化之前的CURD代码:

非常简单,查在之前已经测试了,增加、修改、删除只需要增加这几个方法:save、Update、delete即可。

HibernateTest02

package com.hmc.hibernate;
import com.hmc.hibernate.model.Comment;
import com.hmc.hibernate.model.News;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.Test;
import java.util.List;

/**
 * User:Meice
 * 2017/10/24
 */
public class HibernateTest02 {

    //查
    @Test
    public void testList() {
        //注册服务
        final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
                .configure() // configures settings from hibernate.cfg.xml
                .build();
        //获取sessionFactory
       SessionFactory sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
        //获取session
        Session session = sessionFactory.openSession();
        //查询t_news表中的数据
        List<News> list = session.createQuery(" from News").list();//等价于select * from t_news;
        System.out.println("总条数:"+list.size());

        //关闭session
        session.close();
    }


    //增
    @Test
    public void testAdd() {
        //注册服务
        final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
                .configure() // configures settings from hibernate.cfg.xml
                .build();
        //获取sessionFactory
        SessionFactory sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
        //获取session
        Session session = sessionFactory.openSession();
       // =========================================================
        //实例化对象
        Comment comt = new Comment(2,"Cemei","Hibernate is so easy too!");
       //开启事务
       // session.beginTransaction();
        //保存
        session.save(comt);
        //提交
      //  session.getTransaction().commit();

        //关闭资源
        session.close();

    }



    //改
    @Test
    public void testUpdate() {
        //注册服务
        final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
                .configure() // configures settings from hibernate.cfg.xml
                .build();
        //获取sessionFactory
        SessionFactory sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
        //获取session
        Session session = sessionFactory.openSession();
        // =========================================================
        Comment comt = new Comment(1,"So Hard!","Meice");
        session.beginTransaction();
        session.update(comt);
        session.getTransaction().commit();

        //关闭资源
        session.close();

    }



    //删
    @Test
    public void testDel() {
        //注册服务
        final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
                .configure() // configures settings from hibernate.cfg.xml
                .build();
        //获取sessionFactory
        SessionFactory sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
        //获取session
        Session session = sessionFactory.openSession();
        // =========================================================
       Comment comt = new Comment();
       comt.setId(1);

       session.beginTransaction();
        session.delete(comt);
        session.getTransaction().commit();
        //关闭资源
        session.close();

    }

}

上面代码冗余,类似我们之前简化BaseDao一样,把重复部分封装起来。

2、简化之后的CURD代码:

1)先对重复部分封装
我们创建一个util包,把重复部分封装如下:

HibernateUtil

package com.hmc.hibernate.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;

/**
 * User:Meice
 * 2017/10/24
 */
public class HibernateUtil {

    private  static  SessionFactory sessionFactory = null;
    // A SessionFactory is set up once for an application!
    static {
        //注册服务
        final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
                .configure() // configures settings from hibernate.cfg.xml
                .build();
        //获取sessionFactory
        sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
    }

    //定义获取session的方法getSession()
    public static Session getSession() {
         Session session =   sessionFactory.openSession();
         return session;
    }

    //定义关闭session方法closeSession()
    public static void closeSession(Session session) {
        if(session != null) {
            session.close();
        }
    }

}

这部分是CURD都会重复的部分,封装起来,直接调用。

2)封装后的CURD如下:

package com.hmc.hibernate;
import com.hmc.hibernate.model.Comment;
import com.hmc.hibernate.model.News;
import com.hmc.hibernate.util.HibernateUtil;
import org.hibernate.Session;
import org.junit.Test;
import java.util.List;

/**
 * User:Meice
 * 2017/10/24
 */
public class HibernateUtilTest {
    Session session = null;
    //查
    @Test
    public void testList() {
        try {
            session = HibernateUtil.getSession();
            //获取查询结构,存放集合中
          List<News> list =  session.createQuery("from Comment").list();
            System.out.println(list.get(0));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //关闭session
            HibernateUtil.closeSession(session);
        }

    }

    //增、改
    /*
        session.saveOrUpdate()方法可以同时完成save或者update操作
        如果没有则增加,有则修改,根据Id
     */
    @Test
    public void testAdd() {
        session = HibernateUtil.getSession();
        //开启事务
        session.beginTransaction();
        Comment comt = new Comment(2,"Hibernate is so hard!","Meice");
        session.saveOrUpdate(comt);
        //提交事务
        session.getTransaction().commit();
        //关闭事务
        HibernateUtil.closeSession(session);
    }

    //删
    @Test
    public void del() {
        session = HibernateUtil.getSession();
        Comment comt = new Comment();
        comt.setId(2);
        session.beginTransaction();
        session.delete(comt);
        session.getTransaction().commit();
        HibernateUtil.closeSession(session);
    }
}

似不似清爽了很多~~~~~~~~~~~~~~~~~爽歪歪!

总结:


1、对比之前我们写的BaseDao、JdbcDao,Hibernate底层全部封装好,我们直接调用方法即可;

2、通盘没有一条SQL语句,所以即便不会SQL也可以写CURD;

3、那自己为什么还写那么多JDBC的CURD?个人认为先苦后甜嘛!如果不了解底层的实现,直接用框架,如同一个人的举止谈吐,明明没有丰富的阅历,却还满口人生大道理,就显得缺乏内涵。


3、get与load的区别

获取单个对象的时候,有两种方法:get()和load(),二者有何区别?
区别就在于load()方法是lazy的,只有用的时候才会加载——延迟加载。

HibernateTestLazy

package com.hmc.hibernate;
import com.hmc.hibernate.model.Comment;
import com.hmc.hibernate.util.HibernateUtil;
import org.hibernate.Session;
import org.junit.Test;

/**
 * User:Meice
 * 2017/10/24
 */
public class HibernateTestLazy {
    Session session = null;

    /**
     * 测试查询单个对象时候,get和Load的区别
     */

    //查(单个对象)-get()
    @Test
    public void testGetById () {
        try {
            session = HibernateUtil.getSession();
         Comment comt =  session.get(Comment.class,1);
            /**
             * 直接访问数据库,没有缓存(没有延迟加载)
             * 不管三七二十一,先发一个SQL
             */
            System.out.println(comt.getId());
            System.out.println(comt.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    //查(单个对象)-load()
    @Test
    public void testLoadById() {
        session = HibernateUtil.getSession();
    Comment comt =  session.load(Comment.class,1);
    //  如果获取id,直接从session中获取
        System.out.println(comt.getId());
        /**
         * 如果获取对象的属性在缓存中没有的时候,在发SQL语句查询
         * 只有用的时候在发送SQL,这就是延迟加载,也就是lazy的真实含义
         * 和Scala中变量前面修饰符lazy异曲同工
         */
        System.out.println(comt.getName());
    }

    /**
     * 上面测试结果如下
     * 十月 24, 2017 12:37:42 下午 org.hibernate.dialect.Dialect <init>
     *INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
     * 1
     *Hibernate: select comment0_.id as id1_0_0_, comment0_.content as content2_0_0_, comment0_.com_name as com_name3_0_0_ from t_comment comment0_ where comment0_.id=?
     *Hibernate is so easy!
     */



    //**************************************************************************


    //查(单个对象)-get()
    @Test
    public void testGetById02 () {
        try {
            session = HibernateUtil.getSession();
            Comment comt =  session.get(Comment.class,1);
            /**
             * 无论是否使用get对象,都会发SQL
             */
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 结果:
     * 十月 24, 2017 12:47:16 下午 org.hibernate.dialect.Dialect <init>
     * INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
     * Hibernate: select comment0_.id as id1_0_0_, comment0_.content as content2_0_0_, comment0_.com_name as com_name3_0_0_ from t_comment comment0_ where comment0_.id=?
     */




    //查(单个对象)-load()
    @Test
    public void testLoadById02() {
        session = HibernateUtil.getSession();
        //没有用到load对象,就不发SQL
        Comment comt =  session.load(Comment.class,1);

    }
    /**
     * 结果:INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
     *十月 24, 2017 12:46:19 下午 org.hibernate.dialect.Dialect <init>
     * INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
     */

    /**
     * 总结:表面上看,load只有再用的时候,才和数据库交互,节约资源,似乎更好
     * 但是load也有缺点。。。。。
     */


    //以下测试load的缺点


    public Comment getById(int id) {
        Session session = null;
        try {
             session =   HibernateUtil.getSession();
            /**
             * 如果session关闭后,再返回对象,再次使用时会报错
             * no session异常
             * 解决方案:OpenSessionInView
             */
          Comment comt =  session.load(Comment.class,1);
          return  comt;
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            HibernateUtil.closeSession(session);
        }
        return null;
    }

    @Test
    public void testLoad() {
        Comment comt = getById(1);
        System.out.println(comt);
    }
    /**
     * org.hibernate.LazyInitializationException:
     * could not initialize proxy - no Session
     *
     */
}

所以语言很多地方是相通的。Scala中修饰变量的关键字lazy,不也和这个有异曲同工之妙么?

四、总结

读者看到这里,一定会累了,嘻嘻…..


1、Hibernate的环境搭建,参考官方文档,soEasy!

2、Hibernate确实很简单,不会SQL也可以写CURD;人人都会写了,程序员肿么办……

3、记住这些关键词,也就记住了Hibernate:
配置方面:property、mapping、DDL
API方面:registry、sessionFactory、session、createQuery、saveOrUpdate、delete

4、lazy的含义——延迟加载。用到的时候,在与后台交互,不用就去睡大觉去。好处是节省资源,坏处方法调用时,容易报no session的异常。


哈哈,结束啦。晚安!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值