[ORM框架]Java ORM框架详解之Hibernate

SilenceSea 2018-06-28 阅读


Hibernate框架介绍

什么是Hibernate

 

我们可以从度娘上摘抄这样有关Hibernate的介绍:

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用。

从中,我们可以得出这样的结论:Hibernate是一个轻量级的JDBC封装,也就是说,我们可以使用Hibernate来完成原来我们使用JDBC完成的操作,也就是与数据库的交互操作。它是在dao层去使用的。

什么是ORM

 

对象关系映射(英语:Object Relation Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。 
对象-关系映射,是随着面向对象的软件开发方法发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。 
ORM模型的简单性简化了数据库查询过程。使用ORM查询工具,用户可以访问期望数据,而不必理解数据库的底层结构。 
 

Hibernate的体系结构与开发步骤

Hibernate的体系结构: 
这里写图片描述 
Hibernate开发步骤

  1. 创建持久化类
  2. 创建对象-关系映射文件
  3. 创建Hibernate配置文件
  4. 通过Hibernate API编写访问数据库的代码

这里写图片描述

第一个Hibernate程序(HelloWorld)

创建Eclipse工程并引入相关的jar包

新建Java工程,并添加如下jar包:

  1. {hibernate_home}/lib/required/*.jar
  2. 数据库对应的JDBC驱动(例如mysql-connector-java-5.1.38-bin.jar)

还可以加入日志相关的jar包(不加也可以):

  1. log4j-1.x.x.jar
  2. slf4j-log4j12-1.x.x.jar

这里写图片描述

创建持久化对象:User.java

在cn.itcast.a_helloworld包下创建User类,代码如下:

/**
 * 实体
 */
public class User {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "[User: id=" + id + ", name=" + name + "]";
    }

}

创建对象-关系映射文件:User.hbm.xml

首先在MySQL数据库里面创建一个t_user表,建表语句如下:

create database hibernate_20160926 default character set utf8;
use hibernate_20160926;
create table t_user( id int primary key auto_increment, name varchar(20) );

t_user表创建好之后,我们就要创建相对应的对象-关系映射文件:User.hbm.xml了,注意该映射配置文件与User类要在一起,即同一个包下。

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.a_helloworld">
    <class name="User" table="t_user">
        <id name="id" type="int" column="id">
            <generator class="native"/>
        </id>
        <property name="name" type="string" column="name" />
    </class>
</hibernate-mapping>

关于此映射配置文件中的内容后面会慢慢详解。

创建Hibernate配置文件:hibernate.cfg.xml

我们要在类路径下(即src目录下)创建Hibernate配置文件——hibernate.cfg.xml。  初学者第一次学习Hibernate框架,肯定是不知道如何编写Hibernate配置文件的,但我们可以在{hibernate_home}/project/etc目录下找到开发包给我们提供的Hibernate配置文件,我们只须要拷贝进我们的项目,然后修修改改就可以了。  这样我们的Hibernate配置文件——hibernate.cfg.xml的内容就为:

<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory name="foo">
        <!-- 1. 配置数据库信息 -->
        <!-- 方言(连接的数据库类型) -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="connection.url">jdbc:mysql:///hibernate_20160926</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.username">root</property>
        <property name="connection.password">yezi</property>

        <!-- 2. 其他配置 -->
        <!-- 显示生成的SQL语句 -->
        <property name="hibernate.show_sql">true</property>

        <!-- 3. 导入映射文件 -->
        <mapping resource="cn/itcast/a_helloworld/User.hbm.xml" />

    </session-factory>
</hibernate-configuration>

这里写图片描述  关于此配置文件中的内容后面会慢慢详解。

通过Hibernate API编写访问数据库的代码

编程步骤

  1. 获取Configuration对象
  2. 获取SessionFactory对象
  3. 获取Session,打开事务
  4. 用面向对象的方式操作数据库
  5. 关闭事务,关闭Session

由于是Hibernate入门,所以我不会搞得很复杂,就只是简单的向hibernate_20160926数据库中的t_user表中插入一条记录,然后再取出来而已。  我们在cn.itcast.a_helloworld包下创建一个App类,为了方便测试,我使用的是单元测试。

public class App {

    private static SessionFactory sessionFactory;

    static {
        Configuration cfg = new Configuration();
        cfg.configure("hibernate.cfg.xml"); // 读取指定的主配置文件
        sessionFactory = cfg.buildSessionFactory(); // 根据配置生成Session工厂
    }

    @Test
    public void testSave() {
        User user = new User();
        user.setName("张三");

        // 保存
        Session session = sessionFactory.openSession(); // 打开一个新的Session
        Transaction tx = session.beginTransaction(); // 开启事务

        session.save(user);

        tx.commit(); // 提交事务
        session.close(); // 关闭Session,释放资源(不一定是真正的关闭)
    }

    @Test
    public void testGet() {
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        User user = (User) session.get(User.class, 1); // 获取?
        System.out.println(user);

        tx.commit();
        session.close();
    }
}

测试第一个Hibernate程序

编写完第一个Hibernate程序之后,我们就要来测试了,先测试向hibernate_20160926数据库中的t_user表中插入一条记录,发现报如下异常:

org.hibernate.engine.jndi.JndiException: Error parsing JNDI name [foo]
    at org.hibernate.engine.jndi.internal.JndiServiceImpl.parseName(JndiServiceImpl.java:141)
    at org.hibernate.engine.jndi.internal.JndiServiceImpl.bind(JndiServiceImpl.java:157)
    at org.hibernate.internal.SessionFactoryRegistry.addSessionFactory(SessionFactoryRegistry.java:103)
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:497)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1859)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1930)
    at cn.itcast.a_helloworld.App.<clinit>(App.java:16)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:217)
    at org.junit.runners.BlockJUnit4ClassRunner$1.runReflectiveCall(BlockJUnit4ClassRunner.java:266)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file:  java.naming.factory.initial
    at javax.naming.spi.NamingManager.getInitialContext(Unknown Source)
    at javax.naming.InitialContext.getDefaultInitCtx(Unknown Source)
    at javax.naming.InitialContext.getURLOrDefaultInitCtx(Unknown Source)
    at javax.naming.InitialContext.getNameParser(Unknown Source)
    at org.hibernate.engine.jndi.internal.JndiServiceImpl.parseName(JndiServiceImpl.java:135)
    ... 28 more

错误原因:  在hibernate.cfg.xml文件内容的配置

<session-factory name="foo">
...
</session-factory>

中添加了name属性。  解决办法:  去掉

<session-factory name="foo">
...
</session-factory>

中的name属性。  修改之后,测试,通过,大发!

第二个Hibernate程序——完整的数据库操作(CRUD)

前面我们写了一个Hibernate入门的小程序,但也只是简单的向hibernate_20160926数据库中的t_user表中插入一条记录,然后再取出来而已。现在我们来写第二个Hibernate程序——实现对数据库完整的操作(CRUD)。  我们首先在cn.itcast.b_dao包下创建一个工具类——HibernateUtils.java,该工具类的作用专门用来获取全局唯一的SessionFactory,以及从全局唯一的SessionFactory中打开一个Session。

public class HibernateUtils {

    // SessionFactory全局只需要有一个就可以了,因为它的创建和销毁需要消耗大量的资源,初始化信息会比较多,并且它是线程安全的,可以在多线程的环境下使用它
    private static SessionFactory sessionFactory;

    static {
        // 初始化SessionFactory方式一:
        /*
        Configuration cfg = new Configuration(); // 代表配置文件的一个对象
        cfg.configure(); // 读取默认的配置文件(hibernate.cfg.xml)
        // cfg.configure("hibernate.cfg.xml"); // 读取指定位置的配置文件
        sessionFactory = cfg.buildSessionFactory();
        */

        // 初始化SessionFactory方式二:
        sessionFactory = new Configuration() //
                .configure() //
                .buildSessionFactory(); // 方法链
    }

    /**
     * 获取全局唯一的SessionFactory
     * 
     * @return
     */
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    /**
     * 从全局唯一的SessionFactory中打开一个Session
     * 
     * @return
     */
    public static Session openSession() {
        return sessionFactory.openSession();
    }

}

思考一个问题,若我们要编写代码实现对数据库完整的操作,那么就必定涉及到分页查询,要实现分页查询,我们一定要弄清楚分页设计结构图,要是有人不知道,可翻阅我的记录客户关系管理系统之分页查询。所以我们还要在cn.itcast.b_dao包下创建一个类——QueryResult.java,用于封装查询结果。

public class QueryResult {

    private int count; // 总记录数
    private List list; // 一页的数据

    public QueryResult(int count, List list) {
        this.count = count;
        this.list = list;
    }

    public int getCount() {
        return count;
    }
    public void setCount(int count) {
        this.count = count;
    }
    public List getList() {
        return list;
    }
    public void setList(List list) {
        this.list = list;
    }

}

最后,我们在cn.itcast.b_dao包下创建一个类——UserDao.java。UserDao类里面编写代码实现对数据库完整的操作(CRUD)。

public class UserDao {

    /*
     * 保存
     */
    public void save(User user) {
        Session session = HibernateUtils.openSession();
        try {
            Transaction tx = session.beginTransaction(); // 开启事务
            session.save(user);
            tx.commit(); // 提交事务
        } catch (RuntimeException e) {
            session.getTransaction().rollback(); // 回滚事务
            throw e;
        } finally {
            session.close(); // 关闭session
        }
    }

    /*
     * 更新
     */
    public void update(User user) {
        Session session = HibernateUtils.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();

            session.update(user);// 操作

            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            session.close();
        }
    }

    /*
     * 删除
     */
    public void delete(int id) {
        Session session = HibernateUtils.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();

            Object user = session.get(User.class, id); // 要先获取到这个对象
            session.delete(user); // 删除的是实体对象

            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            session.close();
        }
    }

    /*
     * 根据id查询一个User数据
     */
    public User getById(int id) {
        Session session = HibernateUtils.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            User user = (User) session.get(User.class, id);// 操作
            tx.commit();
            return user;
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            session.close();
        }
    }

    /*
     * 查询所有
     */
    public List<User> findAll() {
        Session session = HibernateUtils.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();

            // 方式一:使用HQL语句
            List<User> list = session.createQuery("FROM User").list(); // 使用HQL查询

            tx.commit();
            return list;
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            session.close();
        }
    }

    /**
     * 分页的查询数据列表
     * @param firstResult 从结果列表中的哪个索引开始取数据
     * @param maxResults 最多取多少条数据
     * @return 一页的数据列表
     */
    @SuppressWarnings("unchecked")
    public QueryResult findAll(int firstResult, int maxResults) {
        Session session = HibernateUtils.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            // 查询一页的数据列表
            // 方式一:
            // Query query = session.createQuery("FROM User");
            // query.setFirstResult(firstResult);
            // query.setMaxResults(maxResults);
            // List<User> list = query.list(); // 使用HQL查询

            // 方式二:方法链
            List<User> list = session.createQuery( //
                    "FROM User") //
                    .setFirstResult(firstResult) // 
                    .setMaxResults(maxResults) //
                    .list();

            // 查询总记录数
            // session.createQuery("SELECT COUNT(*) FROM User").list().get(0);
            // Long count = (Long) session.createQuery("SELECT COUNT(*) FROM User").uniqueResult();
            Long count = (Long) session.createQuery( //
                    "SELECT COUNT(*) FROM User") //
                    .uniqueResult();
            tx.commit();

            // 返回结果
            return new QueryResult(count.intValue(), list);
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            session.close();
        }
    }

}

在如下两个方法中:

public List<User> findAll()
public QueryResult findAll(int firstResult, int maxResults)

我们使用到了Hibernate查询语句——HQL(Hibernate Query Language),后面将会详细讲解,这里稍微了解一下即可,我们只要会用就好。  HQL(Hibernate Query Language)与SQL相似,查询的是对象和对象中的属性,关键字不区分大小写,但类名与属性名区分大小写;而SQL查询的是表和表中的字段,同样也不区分大小写。  接下来,为方便测试,我们使用单元测试来测试以上编写的代码。我们只要右键点击UserDao类→New→JUnit Test Case,如下:  这里写图片描述  接下来会弹出如下对话框:  这里写图片描述  这是要帮我们自动创建UserDao测试类的节奏。  接着点击Next按钮,在弹出的对话框中选中我们要测试的UserDao类中的方法。  这里写图片描述  最后点击Finish完成,这时Eclipse就帮我们自动创建UserDao类的测试类  ——UserDaoTest.java了,然后我们再在其中编写测试代码。

public class UserDaoTest {

    private UserDao userDao = new UserDao();

    @Test
    public void testSave_1() {
        User user = new User();
        user.setName("张三");

        // 保存
        userDao.save(user);
    }

    @Test
    public void testGetById() {
        User user = userDao.getById(1);
        System.out.println(user);
    }

    @Test
    public void testUpdate() {
        // 从数据库中获取一条存在的数据
        User user = userDao.getById(1);
        user.setName("李四");
        // 更新
        userDao.update(user);
    }

    @Test
    public void testDelete() {
        userDao.delete(1);
    }

    // -------------------------

    @Test
    public void testSave_25() {
        for (int i = 1; i <= 25; i++) {
            User user = new User();
            user.setName("test_" + i);

            userDao.save(user); // 保存
        }
    }

    @Test
    public void testFindAll() {
        List<User> list = userDao.findAll();
        for (User user : list) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindAllIntInt() {
        // 查询
        // QueryResult qr = userDao.findAll(0, 10); // 第1页,每页10条
        // QueryResult qr = userDao.findAll(10, 10); // 第2页,每页10条
        QueryResult qr = userDao.findAll(20, 10); // 第3页,每页10条

        // 显示结果
        System.out.println("总记录数:" + qr.getCount());
        for (User user : (List<User>) qr.getList()) {
            System.out.println(user);
        }

    }

}

测试,全部通过,大发!

来源:本站原创

声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。