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

1. 概述

所有的代碼都保證是可運(yùn)行的完整project, 代碼分享在github.com, 平時工作中也可以作為模板代碼Ctrl+c用.
https://github.com/ZhongjunTian/spring-hibernate-examples
本章內(nèi)容在basic-hibernate文件夾下, 總共大約100行代碼.
前提:
需熟悉Java, 了解關(guān)系型數(shù)據(jù)庫(RDBMS)的基本常識, 使用過Spring Boot.
Java 8, Maven 3, Eclipse或者Intellij

2. 優(yōu) & 劣

Hibernate是Java世界最流行的ORM框架之一, 另一個流行的ORM框架是MyBatis.
相對來說, MyBatis更輕量, 簡單, 靈活. 而Hibernate入門就難許多, 優(yōu)秀的Hibernate教程也很少. 但從功能上來說Hibernate更強(qiáng)大, 使用得當(dāng)?shù)脑捒梢杂米钌俚拇a做最多的事情.
Hibernate對比Mybatis:https://www.zhihu.com/question/21104468
Hibernate的優(yōu)缺點:https://www.zhihu.com/question/21607222

3. Hibernate的優(yōu)勢

首先ORM(Object-relational mapping) = 對象關(guān)系映射, 簡單的說就是幫你寫SQL查詢語句里面的廢話

舉個栗子
想象一下我們有如下一個表

CREATE TABLE PERSON (
    id bigint not null,
    name varchar(255) not null,
    address varchar(255) not null
);

3.1 不用Hibernate, 用JDBC的讀取數(shù)據(jù)

那么如果你要查詢數(shù)據(jù), 沒有ORM的話, 你就要寫這個查詢語句
select * from PERSON , 并且建立JDBC連接, 執(zhí)行Statement, 獲取ResultSet, 并且把每一列數(shù)據(jù)從String轉(zhuǎn)換成int, double, String, Date等, 最后放進(jìn)Java的對象里面.
那如果是UPDATE或者INSERT, 并且要連表查詢呢, 那就更麻煩了, 并且代碼很難復(fù)用.

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的讀取數(shù)據(jù)

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

List<Person> persons = personRepository.findAll(); //這么簡單? 嚇?biāo)辣緦殞毩?:)

4.創(chuàng)建我們的第一個Entity

廢話少說, 假設(shè)我們有如下一個表, 這個表基本上涵蓋了常用的數(shù)據(jù)結(jié)構(gòu)
源代碼總共不到100行代碼, 下載代碼 https://github.com/ZhongjunTian/spring-hibernate-examples
用Intellij或者Eclipse打開 /basic-hibernate運(yùn)行main函數(shù), 或者
命令行進(jìn)入 /basic-hibernate 文件夾之后 mvn spring-boot:run 即可運(yùn)行例程, console會輸出讀寫信息。

4.1 表的定義

定義在項目的resources/schma-h2.sql當(dāng)中, 運(yùn)行時Spring Boot會自動啟動java迷你數(shù)據(jù)庫h2, 并且執(zhí)行這個文件.

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的世界中, 每一個表都要有一個對應(yīng)的Entity, 而這個對應(yīng)的Entity也就是Hibernate的核心. 所謂ORM也就是從Java Object映射到關(guān)系數(shù)據(jù)庫(RDMS). 所以這個是非常重要的.
下面這個Entity對應(yīng)上面的表, 覆蓋了所有常用標(biāo)注.

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

重要的標(biāo)注有

@Entity 告訴Hibernate這個類對應(yīng)著數(shù)據(jù)庫的一個表, 默認(rèn)Hibernate會認(rèn)為表的名字與這個class的名字一樣, Hibernate核心代碼會掃描整個class.

@Id 標(biāo)明這個是數(shù)據(jù)庫表的主鍵, 一般用Long類型即可(一定不要使用int long等類型).
使用Spring的情況下, 通常我們很少會見到傳統(tǒng)數(shù)據(jù)庫的insert與update操作, 我們通常會使用repository.save(entity). 那Hibernate如何判斷執(zhí)行Insert還是update操作呢? 很簡單如果一個entity.id == null說明需要insert數(shù)據(jù), 反之則為update操作(update需要主鍵).

@GeneratedValue 標(biāo)明主鍵的生成方式, 這里我們用的默認(rèn)的值, 也就是主鍵由數(shù)據(jù)庫自動生成 (對應(yīng)mysql里面的auto increment選項). 當(dāng)hibernate執(zhí)行SQL語句的時候并不會插入id, 而是由數(shù)據(jù)庫自動增加. 另外還有一種高性能的id生成策略叫hi lo, 可以支持批處理, 這里不贅述.

@Temporal(TemporalType.TIMESTAMP) 標(biāo)明Date在數(shù)據(jù)庫里面的精度, 因為我們用的是java.util.Date類型. 這個是精度非常高的類型, 可以表示yyyy/MM/dd HH:mm:sss. 而在數(shù)據(jù)庫對應(yīng)的列可能會有 Date, Time, TimeStamp三種不同的類型, 這里只要如實填寫即可. 其中Date只有日期, Time只有時間, TimeStamp兩者都有. 比如設(shè)置為TimeStamp.Date, 那么這個Date birthDate為 2000/1/1 12:22:22, Hibernate也只會保存前面的2000/1/1到數(shù)據(jù)庫.

常用的標(biāo)注有

@Column 在java 類里面變量與表里面的某一列的名字不一樣的時候使用. 這里 @Column(name = "userName"), 在Hibernate生成SQL語句的時候, 就會使用user_name而不是name;(這里Spring Boot里面默認(rèn)把java的駝峰命名userName轉(zhuǎn)換成了數(shù)據(jù)庫最流行的user_name風(fēng)格)
沒有@Column的時候Hibernate會默認(rèn)Java類成員的名字與表里面的名字是一樣的. 比如id 對應(yīng)表里面 id, birthDate 對應(yīng)表里面 birth_date.

@Transient 也是一個經(jīng)常被用到的標(biāo)注. Transient在英文中的意思是'短暫的', 它的反義詞剛好是Persistent'持久的, 持續(xù)化的'. 正如其名, 被@Transient標(biāo)注的成員不會被Hibernate管理, 也就是無法被Hibernate保存到數(shù)據(jù)庫, 也不會從數(shù)據(jù)庫里面讀取. (類似于Jackson里面的@JsonIgnore). 只是Java類里面的一個普通成員, Hibernate會無視它.

@Lob lob是large object的意思, 也就是超大的object, 比如圖片,文件,超長的String等等, 統(tǒng)統(tǒng)都可以簡單的塞進(jìn)數(shù)據(jù)庫. 我這里用的java類型是String clob, 其實也可以是 byte[], char[]等等.

數(shù)據(jù)庫類型映射到Java類型

我們可以仔細(xì)對比一下3.1的表的定義和3.2的entity定義, 我們把數(shù)據(jù)庫里面bigint, varchar, datetime等類型映射成為了Java里面的Long, String, Date.
這里有兩個需要注意的地方, id一定不能用long, 必須用Long.
其他的成員如果在數(shù)據(jù)庫里面有not null限制, 那么可以用 int/long/float/double這種原始數(shù)據(jù)類型, 否則建議用Integer/Long這種 (比如3.1的balance decimal null)

5. 讀寫數(shù)據(jù)庫

首先我們定義一個Spring的數(shù)據(jù)倉庫. 直接創(chuàng)建如下接口即可. 其中<Person, Long>意思是Entity為Account, 并且id的數(shù)據(jù)類型為Long. 當(dāng)Spring JPA的核心代碼掃描到Person Repository這個接口之后, 會自動創(chuàng)建一個SimpleJpaRepository的實例.

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

下面的personRepository其實就是個SimpleJpaRepository的實例.
剩下的代碼就很簡單了, 增查改刪一氣呵成, 毫無廢話. 強(qiáng)烈建議讀者在IDE里面運(yùn)行完整版的代碼, 可以自己試著改一改, 玩一玩.
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(); 就相當(dāng)于select * from person;
personRepository.save(account);就能insert或者update表. 當(dāng)account里面的id == null的時候是insert 相當(dāng)于執(zhí)行SQL語句insert into person ... . 不為null, 比如 id == 1 的時候?qū)?yīng)update, 相當(dāng)于執(zhí)行update person ... where id=1
personRepository.delete(acct) 根據(jù)entity的id做刪除操作, 比如 acct.id等于1, 那么執(zhí)行就的是 delete from account where id=1;

雪地打滾360度跪求讀者提意見!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末番川,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖鲁驶,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件做入,死亡現(xiàn)場離奇詭異,居然都是意外死亡缔御,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門妇蛀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耕突,“玉大人笤成,你說我怎么就攤上這事【熳拢” “怎么了炕泳?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長上祈。 經(jīng)常有香客問我培遵,道長,這世上最難降的妖魔是什么登刺? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任籽腕,我火速辦了婚禮,結(jié)果婚禮上纸俭,老公的妹妹穿的比我還像新娘皇耗。我一直安慰自己,他們只是感情好揍很,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布郎楼。 她就那樣靜靜地躺著,像睡著了一般女轿。 火紅的嫁衣襯著肌膚如雪箭启。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天蛉迹,我揣著相機(jī)與錄音傅寡,去河邊找鬼。 笑死北救,一個胖子當(dāng)著我的面吹牛荐操,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播珍策,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼托启,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了攘宙?” 一聲冷哼從身側(cè)響起屯耸,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蹭劈,沒想到半個月后疗绣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡铺韧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年多矮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡塔逃,死狀恐怖讯壶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情湾盗,我是刑警寧澤伏蚊,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站格粪,受9級特大地震影響丙挽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜匀借,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望平窘。 院中可真熱鬧吓肋,春花似錦、人聲如沸瑰艘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽紫新。三九已至均蜜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芒率,已是汗流浹背囤耳。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留偶芍,地道東北人充择。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像匪蟀,于是被迫代替她去往敵國和親椎麦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理材彪,服務(wù)發(fā)現(xiàn)观挎,斷路器,智...
    卡卡羅2017閱讀 134,628評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,773評論 6 342
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法段化,類相關(guān)的語法嘁捷,內(nèi)部類的語法,繼承相關(guān)的語法穗泵,異常的語法普气,線程的語...
    子非魚_t_閱讀 31,598評論 18 399
  • 暖暖的秋日下,他佃延,胖乎乎的小手用稚嫩的心寫著滿心的感動现诀! 她在想:明天我能呵護(hù)成功嗎夷磕? 她垂下眼簾,停下筆尖仔沿,似乎...
    蘇打水2016閱讀 415評論 0 1
  • Python裝飾器 Examples: 第一步:最簡單的函數(shù)坐桩,準(zhǔn)備附加額外功能 第二步:使用裝飾函數(shù)在函數(shù)執(zhí)行前和...
    很少更新了閱讀 275評論 0 1