6.2 Spring Boot集成jpa

6.2 Spring Boot集成jpa

Java持久化API(JPA琼讽,Java Persistence API)是一個將對象映射為關(guān)系數(shù)據(jù)庫的標(biāo)準(zhǔn)技術(shù)扮惦。JPA通過注解或XML描述ORM(Object Relationship Mapping订歪,對象-關(guān)系表的映射關(guān)系)橄碾,并將運行期的實體對象持久化到數(shù)據(jù)庫中由境。

其中,SQL(結(jié)構(gòu)化查詢語言, Structured Query Language)拼卵,是持久化操作中很重要的一個方面奢浑,通過面向?qū)ο蠖敲嫦驍?shù)據(jù)庫的查詢語言查詢數(shù)據(jù),避免程序的SQL語句的緊耦合腋腮。

JPA的主要目標(biāo)之一就是提供更加簡單的編程模型:在JPA框架下創(chuàng)建實體和創(chuàng)建Java 類一樣簡單雀彼,沒有任何的約束和限制,只需要使用 javax.persistence.Entity進行注解即寡。

JPA的框架和接口也都非常簡單徊哑,沒有太多特別的規(guī)則和設(shè)計模式的要求,開發(fā)者可以很容易的掌握聪富。

JPA基于非侵入式原則設(shè)計莺丑,因此可以很容易的和其它框架或者容器集成。

在SpringBoot中墩蔓,如果我們想使用JPA作為數(shù)據(jù)庫ORM層梢莽,很簡單,我們只需要添加spring-boot-starter-data-jpa依賴即可:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

spring-boot-starter-data-jpa提供了以下關(guān)鍵依賴:

  • Hibernate - 一個非常流行的JPA實現(xiàn)奸披。
  • Spring Data JPA - 讓實現(xiàn)基于JPA的repositories更容易昏名。
  • Spring ORMs - Spring框架的ORM。

詳細的依賴樹如下

在SpringBoot中阵面,模塊依賴圖如下:

當(dāng)然葡粒,還有數(shù)據(jù)源的一些配置:

#mysql
spring.datasource.url = jdbc:mysql://localhost:3306/teda?useUnicode=true&characterEncoding=UTF8
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.max-active=0
spring.datasource.max-idle=0
spring.datasource.min-idle=0
spring.datasource.max-wait=10000
spring.datasource.max-wait-millis=31536000

# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

在實體類上使用@NamedQuery

我們可以直接在實體類上份殿,定義查詢方法。代碼示例:

package com.steda.entity

import java.util.Date
import javax.persistence._

import scala.beans.BeanProperty

@Entity
@NamedQuery(name = "findByState",
  query = "select t from TedaCase t where t.state = ?1")
class TedaCase {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @BeanProperty
  var id: Long = _
  @BeanProperty
  var name: String = _
  ...
}

然后嗽交,我們繼承CrudRepository接口之后卿嘲,定義一個同名的方法findByState,就可以直接用這個方法了夫壁,它會執(zhí)行我們定義好的查詢語句并返回結(jié)果拾枣。代碼示例:

package com.steda.dao


import com.steda.entity.TedaCase
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.CrudRepository

trait TedaCaseDao extends CrudRepository[TedaCase, java.lang.Long] {
  def findByState(state: Integer): java.util.List[TedaCase]
  ...
}


同樣的,如果我們想定義多條NamedQuery盒让,也是可以的梅肤。代碼示例如下:

@NamedQueries({ 
        @NamedQuery(name="findAllUser",query="select u from User u"), 
        @NamedQuery(name="findUserWithId",query="select u from User u WHERE u.id = ?1"), 
        @NamedQuery(name="findUserWithName",query="select u from User u WHERE u.name = :name") 
         
}) 

其背后的方法的生成自動生成原理,是由類org.hibernate.jpa.spi.AbstractEntityManagerImpl來完成的邑茄。實質(zhì)思想就是通過注解在運行時動態(tài)生成對應(yīng)的查詢方法姨蝴,實現(xiàn)了元編程。

在接口方法上使用@Query

指定了nativeQuery = true肺缕,即使用原生的sql語句查詢左医。使用原生的sql語句, 根據(jù)數(shù)據(jù)庫的不同,在sql的語法或結(jié)構(gòu)方面可能有所區(qū)別同木。舉例如下:

@Query(value="select * from param_json_template order by id desc",nativeQuery = true)

默認false浮梢。我們可以使用java對象作為表名來查詢。但是要注意彤路,就不能使用原生sql的select * from 秕硝,要使用java字段名。舉例如下:

@Query(value="select  id,paramObject,paramJsonTemplateStr  from ParamJsonTemplate p where p.paramObject like %:paramObject% order by p.id desc")

如果我們想指定參數(shù)名洲尊,可以通過@Param(value = "paramObject") 來指定方法變量名远豺,然后在查詢語句中,使用:paramObject來使用該變量坞嘀。
舉例如下:

@Query(value="select  id,paramObject,paramJsonTemplateStr  from ParamJsonTemplate p where p.paramObject like %:paramObject% order by p.id desc")
  def findByParamObject(@Param(value = "paramObject") paramObject:String): java.util.List[ParamJsonTemplate]

完整實例代碼:

package com.steda.dao

import org.springframework.data.repository.CrudRepository
import com.steda.entity.ParamJsonTemplate
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param

trait ParamJsonTemplateDao extends CrudRepository[ParamJsonTemplate, java.lang.Long] {


  @Query(value="select * from param_json_template order by id desc",nativeQuery = true)
  def findAll(): java.util.List[ParamJsonTemplate]


  @Query(value="select  id,paramObject,paramJsonTemplateStr  from ParamJsonTemplate p where p.paramObject like %:paramObject% order by p.id desc")
  def findByParamObject(@Param(value = "paramObject") paramObject:String): java.util.List[ParamJsonTemplate]

  def save(p: ParamJsonTemplate): ParamJsonTemplate

}

JpaRepository 創(chuàng)建查詢的順序

Spring Data JPA 在為接口創(chuàng)建代理對象時躯护,可以利用創(chuàng)建方法進行查詢,也可以利用@Query注釋進行查詢姆吭,那么如果在命名規(guī)范的方法上使用了@Query榛做,那spring data jpa是執(zhí)行我們定義的語句進行查詢唁盏,還是按照規(guī)范的方法進行查詢呢内狸?它該優(yōu)先采用哪種策略呢?

QueryLookupStrategy定義了3個屬性key厘擂,用以指定查找的順序昆淡。它有如下三個取值:

1:create-if-not-found:如果方法通過@Query指定了查詢語句,則使用該語句實現(xiàn)查詢刽严;如果沒有昂灵,則查找是否定義了符合條件的命名查詢避凝,如果找到,則使用該命名查詢眨补;如果兩者都沒有找到管削,則通過解析方法名字來創(chuàng)建查詢。這是 query-lookup-strategy 屬性的默認值撑螺。

2:create:通過解析方法名字來創(chuàng)建查詢含思。即使有符合的命名查詢,或者方法通過 @Query指定的查詢語句甘晤,都將會被忽略

3:use-declared-query:如果方法通過@Query指定了查詢語句含潘,則使用該語句實現(xiàn)查詢;如果沒有线婚,則查找是否定義了符合條件的命名查詢遏弱,如果找到,則使用該命名查詢塞弊;如果兩者都沒有找到漱逸,則拋出異常。

Spring Data JPA 在org.springframework.data.repository.query.QueryLookupStrategy中定義了如下策略枚舉值:

CREATE, USE_DECLARED_QUERY, CREATE_IF_NOT_FOUND

其源碼如下:

/*
 * Copyright 2008-2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.repository.query;

import java.lang.reflect.Method;
import java.util.Locale;

import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.util.StringUtils;

/**
 * Strategy interface for which way to lookup {@link RepositoryQuery}s.
 * 
 * @author Oliver Gierke
 */
public interface QueryLookupStrategy {

    public static enum Key {

        CREATE, USE_DECLARED_QUERY, CREATE_IF_NOT_FOUND;

        /**
         * Returns a strategy key from the given XML value.
         * 
         * @param xml
         * @return a strategy key from the given XML value
         */
        public static Key create(String xml) {

            if (!StringUtils.hasText(xml)) {
                return null;
            }

            return valueOf(xml.toUpperCase(Locale.US).replace("-", "_"));
        }
    }

    /**
     * Resolves a {@link RepositoryQuery} from the given {@link QueryMethod} that can be executed afterwards.
     * 
     * @param method
     * @param metadata
     * @param namedQueries
     * @return
     */
    RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries);
}

具體的實現(xiàn)邏輯居砖,在org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy中虹脯。其關(guān)鍵方法如下:


    /**
     * Creates a {@link QueryLookupStrategy} for the given {@link EntityManager} and {@link Key}.
     * 
     * @param em must not be {@literal null}.
     * @param key may be {@literal null}.
     * @param extractor must not be {@literal null}.
     * @param evaluationContextProvider must not be {@literal null}.
     * @return
     */
    public static QueryLookupStrategy create(EntityManager em, Key key, QueryExtractor extractor,
            EvaluationContextProvider evaluationContextProvider) {

        Assert.notNull(em, "EntityManager must not be null!");
        Assert.notNull(extractor, "QueryExtractor must not be null!");
        Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null!");

        switch (key != null ? key : Key.CREATE_IF_NOT_FOUND) {
            case CREATE:
                return new CreateQueryLookupStrategy(em, extractor);
            case USE_DECLARED_QUERY:
                return new DeclaredQueryLookupStrategy(em, extractor, evaluationContextProvider);
            case CREATE_IF_NOT_FOUND:
                return new CreateIfNotFoundQueryLookupStrategy(em, extractor, new CreateQueryLookupStrategy(em, extractor),
                        new DeclaredQueryLookupStrategy(em, extractor, evaluationContextProvider));
            default:
                throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key));
        }
    }


從這句switch (key != null ? key : Key.CREATE_IF_NOT_FOUND),我們可以看出默認值是CREATE_IF_NOT_FOUND奏候。

小結(jié)

本章示例工程源代碼:

https://github.com/EasySpringBoot/teda

參考資料:
1.http://docs.jboss.org/hibernate/orm/5.2/quickstart/html_single/
2.https://spring.io/guides/gs/accessing-data-jpa/
3.http://baike.baidu.com/item/JPA

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末循集,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蔗草,更是在濱河造成了極大的恐慌咒彤,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咒精,死亡現(xiàn)場離奇詭異镶柱,居然都是意外死亡,警方通過查閱死者的電腦和手機模叙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門歇拆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人范咨,你說我怎么就攤上這事故觅。” “怎么了渠啊?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵输吏,是天一觀的道長。 經(jīng)常有香客問我替蛉,道長贯溅,這世上最難降的妖魔是什么拄氯? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮它浅,結(jié)果婚禮上译柏,老公的妹妹穿的比我還像新娘。我一直安慰自己姐霍,他們只是感情好艇纺,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著邮弹,像睡著了一般黔衡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上腌乡,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天盟劫,我揣著相機與錄音,去河邊找鬼与纽。 笑死侣签,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的急迂。 我是一名探鬼主播影所,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼僚碎!你這毒婦竟也來了猴娩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤勺阐,失蹤者是張志新(化名)和其女友劉穎卷中,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渊抽,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡蟆豫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了懒闷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片十减。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖愤估,靈堂內(nèi)的尸體忽然破棺而出帮辟,到底是詐尸還是另有隱情,我是刑警寧澤灵疮,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布织阅,位于F島的核電站壳繁,受9級特大地震影響震捣,放射性物質(zhì)發(fā)生泄漏荔棉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一蒿赢、第九天 我趴在偏房一處隱蔽的房頂上張望润樱。 院中可真熱鬧,春花似錦羡棵、人聲如沸壹若。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽店展。三九已至,卻和暖如春秃流,著一層夾襖步出監(jiān)牢的瞬間赂蕴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工舶胀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留概说,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓嚣伐,卻偏偏與公主長得像糖赔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子轩端,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

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