Spring Hibernate 5 快速入门(一) 最基本的Entity和增查改删

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;(这里Spring Boot里面默认把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里面运行完整版的代码, 可以自己试着改一改, 玩一玩.
Talk is cheap, show you the code.

   @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;

雪地打滚360度跪求读者提意见!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 148,637评论 1 318
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 63,443评论 1 266
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 99,164评论 0 218
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,075评论 0 188
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,080评论 1 266
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,365评论 1 184
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,901评论 2 283
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,649评论 0 176
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,122评论 0 223
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,734评论 2 225
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,093评论 1 236
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,548评论 2 222
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,028评论 3 216
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,765评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,291评论 0 178
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,162评论 2 239
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,293评论 2 242

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,036评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,145评论 6 345
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,265评论 18 399
  • 暖暖的秋日下,他,胖乎乎的小手用稚嫩的心写着满心的感动! 她在想:明天我能呵护成功吗? 她垂下眼帘,停下笔尖,似乎...
    苏打水2016阅读 391评论 0 1
  • Python装饰器 Examples: 第一步:最简单的函数,准备附加额外功能 第二步:使用装饰函数在函数执行前和...
    很少更新了阅读 253评论 0 1