>>分享JPA、Hibernate、Mybatis对象持久化技术,对《精通JPA与Hibernate:Java对象持久化详解》提供技术支持 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 23818 个阅读者 刷新本主题
 * 贴子主题:  Spring Hibernate5入门 回复文章 点赞(0)  收藏  
作者:Jacky    发表时间:2018-11-30 09:47:33     消息  查看  搜索  好友  邮件  复制  引用

1. 概述

所有的代码都保证是可运行的完整project, 代码分享在github.com, 平时工作中也可以作为模板代码Ctrl+c用.
https://github.com/ZhongjunTian/spring-hibernate-examples
本章内容在basic-hibernate文件夹下, 总共大约100行代码.

前提:

需熟悉Java, 了解关系型数据库(RDBMS)的基本常识, 使用过Spring Boot.
Java 8, Maven 3, Eclipse或者Intellij

2. 优 & 劣

Hibernate是Java世界最流行的ORM框架之一, 另一个流行的ORM框架是MyBatis.
相对来说, MyBatis更轻量, 简单, 灵活. 而Hibernate入门就难许多, 优秀的Hibernate教程也很少. 但从功能上来说Hibernate更强大, 使用得当的话可以用最少的代码做最多的事情.
Hibernate对比Mybatis:https://www.zhihu.com/question/21104468
Hibernate的优缺点:https://www.zhihu.com/question/21607222

3. Hibernate的优势

首先ORM(Object-relational mapping) = 对象关系映射, 简单的说就是帮你写SQL查询语句里面的废话

举个例子

想象一下我们有如下一个表
CREATE TABLE PERSON (
    id bigint not null,
    name varchar(255) not null,
    address varchar(255) not null
);

3.1 不用Hibernate, 用JDBC的读取数据

那么如果你要查询数据, 没有ORM的话, 你就要写这个查询语句:

select * from PERSON ,

并且建立JDBC连接, 执行Statement, 获取ResultSet, 并且把每一列数据从String转换成int, double, String, Date等, 最后放进Java的对象里面.

那如果是UPDATE或者INSERT, 并且要连表查询呢, 那就更麻烦了, 并且代码很难复用.

public static void main(String[] args) {
        String driver = "com.mysql.jdbc.Driver";
        String dbName = "spring";
        String passwrod = "root";
        String userName = "root";
        String url = "jdbc:mysql://localhost:3308/" + dbName;
        String sql = "select * from users";

        try {
            Class.forName(driver);
            try (
                    Connection conn = DriverManager.getConnection(url, userName,passwrod);
                    PreparedStatement ps = conn.prepareStatement(sql);
                    ResultSet rs = ps.executeQuery();
            ) {
                while (rs.next()) {
                    System.out.println("id : " + rs.getInt(1) + " name : "
                            + rs.getString(2) + " address : " + rs.getString(3));
                }

            }catch (SQLException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
}

3.2 用Hibernate的读取数据

但是一旦有了Hibernate以及Spring Reposiroty, 你只要在class Person里面用java注释定义好, 那么你只用一行代码就能完成上面所有事情

List<Person> persons = personRepository.findAll(); //这么简单? 吓死本宝宝了 :)

4.创建我们的第一个Entity

废话少说, 假设我们有如下一个表, 这个表基本上涵盖了常用的数据结构
源代码总共不到100行代码, 下载代码 https://github.com/ZhongjunTian/spring-hibernate-examples
用Intellij或者Eclipse打开 /basic-hibernate运行main函数, 或者
命令行进入 /basic-hibernate 文件夹之后 mvn spring-boot:run 即可运行例程, console会输出读写信息。

4.1 表的定义

定义在项目的resources/schma-h2.sql当中, 运行时Spring Boot会自动启动java迷你数据库h2, 并且执行这个文件.

CREATE TABLE Person (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY,
    user_name varchar(255) not null,
    birth_date DATETIME null,
    money decimal null,
    gender char(1) null,
    clob clob null
);

4.2 定义Entity

在Hibernate的世界中, 每一个表都要有一个对应的Entity, 而这个对应的Entity也就是Hibernate的核心. 所谓ORM也就是从Java Object映射到关系数据库(RDMS). 所以这个是非常重要的.
下面这个Entity对应上面的表, 覆盖了所有常用标注.

@Entity
public class Person {
    @Id
    @GeneratedValue
    Long id;

    @Column(name = "userName")
    String name;

    @Temporal(TemporalType.TIMESTAMP)
    Date birthDate;

    BigDecimal money;

    Character gender;

    @Transient
    boolean gay;

    @Lob
    String clob;
}

重要的标注有:

  • @Entity 告诉Hibernate这个类对应着数据库的一个表, 默认Hibernate会认为表的名字与这个class的名字一样, Hibernate核心代码会扫描整个class.
  • @Id 标明这个是数据库表的主键, 一般用Long类型即可(一定不要使用int long等类型).
    使用Spring的情况下, 通常我们很少会见到传统数据库的insert与update操作, 我们通常会使用repository.save(entity). 那Hibernate如何判断执行Insert还是update操作呢? 很简单如果一个entity.id == null说明需要insert数据, 反之则为update操作(update需要主键).
  • @GeneratedValue 标明主键的生成方式, 这里我们用的默认的值, 也就是主键由数据库自动生成 (对应mysql里面的auto increment选项). 当hibernate执行SQL语句的时候并不会插入id, 而是由数据库自动增加. 另外还有一种高性能的id生成策略叫hi lo, 可以支持批处理, 这里不赘述.
  • @Temporal(TemporalType.TIMESTAMP) 标明Date在数据库里面的精度, 因为我们用的是java.util.Date类型. 这个是精度非常高的类型, 可以表示yyyy/MM/dd HH:mm:sss. 而在数据库对应的列可能会有 Date, Time, TimeStamp三种不同的类型, 这里只要如实填写即可. 其中Date只有日期, Time只有时间, TimeStamp两者都有. 比如设置为TimeStamp.Date, 那么这个Date birthDate为 2000/1/1 12:22:22, Hibernate也只会保存前面的2000/1/1到数据库.

常用的标注有

  • @Column 在java 类里面变量与表里面的某一列的名字不一样的时候使用. 这里 @Column(name = "userName"), 在Hibernate生成SQL语句的时候, 就会使用user_name而不是name;(这里SpringBoot里面默认把java的驼峰命名userName转换成了数据库最流行的user_name风格)
    没有@Column的时候Hibernate会默认Java类成员的名字与表里面的名字是一样的. 比如id 对应表里面 id, birthDate 对应表里面 birth_date.
  • @Transient 也是一个经常被用到的标注. Transient在英文中的意思是'短暂的', 它的反义词刚好是Persistent'持久的, 持续化的'. 正如其名, 被@Transient标注的成员不会被Hibernate管理, 也就是无法被Hibernate保存到数据库, 也不会从数据库里面读取. (类似于Jackson里面的@JsonIgnore). 只是Java类里面的一个普通成员, Hibernate会无视它.
  • @Lob lob是large object的意思, 也就是超大的object, 比如图片,文件,超长的String等等, 统统都可以简单的塞进数据库. 我这里用的java类型是String clob, 其实也可以是 byte[], char[]等等.
    数据库类型映射到Java类型
我们可以仔细对比一下3.1的表的定义和3.2的entity定义, 我们把数据库里面bigint, varchar, datetime等类型映射成为了Java里面的Long, String, Date.
这里有两个需要注意的地方, id一定不能用long, 必须用Long.
其他的成员如果在数据库里面有not null限制, 那么可以用 int/long/float/double这种原始数据类型, 否则建议用Integer/Long这种 (比如3.1的balance decimal null)

5. 读写数据库

首先我们定义一个Spring的数据仓库. 直接创建如下接口即可. 其中<Person, Long>意思是Entity为Account, 并且id的数据类型为Long. 当Spring JPA的核心代码扫描到Person Repository这个接口之后, 会自动创建一个SimpleJpaRepository的实例.

public interface PersonRepository extends JpaRepository<Person,Long> {
}

下面的personRepository其实就是个SimpleJpaRepository的实例.
剩下的代码就很简单了, 增查改删一气呵成, 毫无废话. 强烈建议读者在IDE里面运行完整版的代码, 可以自己试着改一改, 玩一玩.

   @Autowired
    PersonRepository personRepository;

    public void run(String... strings) throws Exception {
        System.out.println("Start!");
        //增
        Person person = Person.createAccount();
        personRepository.save(person);
        System.out.println();

        //查
        Person acct = personRepository.findAll().get(0);
        System.out.println(String.format("Person after creation: %s, %s, %s, %s, %s, %s, %s",
                acct.id, acct.name, acct.birthDate, acct.money, acct.gender, acct.gay, acct.clob));

        //改
        acct.name = "newName";
        personRepository.save(acct);
        acct = personRepository.findAll().get(0);
        System.out.println("UserName after update: "+acct.name);

        //删
        personRepository.delete(acct);
        List<Person> people = personRepository.findAll();
        System.out.println("Size after deletion: "+ people.size());

    }

personRepository.findAll(); 就相当于select * from person;
personRepository.save(account);就能insert或者update表. 当account里面的id == null的时候是insert 相当于执行SQL语句insert into person ... . 不为null, 比如 id == 1 的时候对应update, 相当于执行update person ... where id=1
personRepository.delete(acct) 根据entity的id做删除操作, 比如 acct.id等于1, 那么执行就的是 delete from account where id=1;

----------------------------------------
作者:JonTian
原文链接:https://www.jianshu.com/p/64fef4fa5645



程序猿的技术大观园:www.javathinker.net



[这个贴子最后由 flybird 在 2020-02-24 10:32:43 重新编辑]
  Java面向对象编程-->按面向对象开发的基础范例
  JavaWeb开发-->访问数据库(Ⅰ)
  JSP与Hibernate开发-->JPA API的高级用法
  Java网络编程-->Socket用法详解
  精通Spring-->Vue简介
  Vue3开发-->Vue CLI脚手架工具
  孙卫琴的Java对象持久化技术之旅-视频讲座
  孙卫琴的《精通JPA与Hibernate》的读书笔记: 用JPQL批量处理...
  十分钟入门 JPA
  用注解和XML描述对象-关系映射的区别,和优缺点
  用CriteriaBuilder进行动态查询
  Hibernate中通过FetchProfile的方式实现动态数据获取
  EntityTransaction 与UserTransaction的区别
  征求意见:正在写一本新书《精通JPA与Hibernate》
  盘点Hibernate的值类型映射技巧
  深入理解乐观锁和悲观锁
  JPA API入门
  Hibernate配置文件中的hibernate.hbm2ddl.auto属性详解
  JPA (Java Persistence API)2.2 的一些新特性
  Hibernate 5的新特性
  在看孙老师的hibernate书。。。
  更多...
 IPIP: 已设置保密
树形列表:   
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。