SpringBoot--實戰(zhàn)開發(fā)--整合Spring Data JPA(十一)

一窿克、SpringData簡介

Spring Data是一個用于簡化數(shù)據(jù)庫訪問,并支持云服務(wù)的開源框架戚啥。其主要目標(biāo)是使得對數(shù)據(jù)的訪問變得方便快捷。

  1. Spring Data JPA能干什么
      可以極大的簡化JPA的寫法锉试,可以在幾乎不用寫實現(xiàn)的情況下猫十,實現(xiàn)對數(shù)據(jù)的訪問和操作。除了CRUD外呆盖,還包括如分頁拖云、排序等一些常用的功能。
  2. Spring Data JPA 有什么
      主要來看看Spring Data JPA提供的接口应又,也是Spring Data JPA的核心概念:
        1:Repository:最頂層的接口宙项,是一個空的接口,目的是為了統(tǒng)一所有Repository的類型株扛,且能讓組件掃描的時候自動識別尤筐。
        2:CrudRepository :是Repository的子接口汇荐,提供CRUD的功能
        3:PagingAndSortingRepository:是CrudRepository的子接口,添加分頁和排序的功能
        4:JpaRepository:是PagingAndSortingRepository的子接口叔磷,增加了一些實用的功能拢驾,比如:批量操作等。
        5:JpaSpecificationExecutor:用來做負(fù)責(zé)查詢的接口
        6:Specification:是Spring Data JPA提供的一個查詢規(guī)范改基,要做復(fù)雜的查詢繁疤,只需圍繞這個規(guī)范來設(shè)置查詢條件即可
  3. 特征
    1)強大的存儲庫和自定義對象映射抽象
    2)從存儲庫方法名稱中進(jìn)行動態(tài)查詢導(dǎo)出
    3)實現(xiàn)域基類提供基本屬性
    4)支持透明審核(創(chuàng)建,最后更改)
    5)集成自定義存儲庫代碼的可能性
    6)Easy Spring通過JavaConfig和自定義XML命名空間進(jìn)行集成
    7)與Spring MVC控制器進(jìn)行高級集成

二秕狰、Maven依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

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

三稠腊、application.properties配置

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/iotManager?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=1234
#自動建表
spring.jpa.hibernate.ddl-auto=update
#設(shè)置數(shù)據(jù)庫方言
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
#打印sql
spring.jpa.show-sql=true

Jpa的配置后 jpa.hibernate.ddl-auto= update,在其他低版本的SpringBoot中也有使用spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop 這種配置的,具體根據(jù)版本而定鸣哀。該配置的主要作用是:自動創(chuàng)建架忌、更新、驗證數(shù)據(jù)庫結(jié)構(gòu)
1我衬、create:每次加載hibernate時都會刪除上一次的生成的表叹放,然后根據(jù)你的model類再重新來生成新表,哪怕兩次沒有任何改變也要這樣執(zhí)行挠羔,這就是導(dǎo)致數(shù)據(jù)庫表數(shù)據(jù)丟失的一個重要原因(一般只會在第一次創(chuàng)建時使用)
2井仰、create-drop:每次加載hibernate時根據(jù)model類生成表,但是sessionFactory一關(guān)閉,表就自動刪除
3破加、update:最常用的屬性俱恶,第一次加載hibernate時根據(jù)model類會自動建立起表的結(jié)構(gòu)(前提是先建立好數(shù)據(jù)庫),以后加載hibernate時根據(jù)model類自動更新表結(jié)構(gòu)范舀,即使表結(jié)構(gòu)改變了但表中的行仍然存在不會刪除以前的行合是。要注意的是當(dāng)部署到服務(wù)器后,表結(jié)構(gòu)是不會被馬上建立起來的锭环,是要等應(yīng)用第一次運行起來后才會
4聪全、validate:每次加載hibernate時,驗證創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu)田藐,只會和數(shù)據(jù)庫中的表進(jìn)行比較荔烧,不會創(chuàng)建新表,但是會插入新值汽久。

四鹤竭、創(chuàng)建實體

創(chuàng)建一個User類,配置好上面的信息后景醇,啟動項目臀稚,對應(yīng)的數(shù)據(jù)庫就會自動生成對應(yīng)的表結(jié)構(gòu)。@Table三痰、@Entity吧寺、@Id等注解是jpa的相關(guān)知識窜管。

@Table(name = "t_user")
@Entity
@Data
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 設(shè)為主鍵 唯一不能為空
     * nullable 是否可以為空
     * unique 唯一
     * @GeneratedValue
     * 就是為一個實體生成一個唯一標(biāo)識的主鍵
     * (JPA要求每一個實體Entity,必須有且只有一個主鍵)
     * @GeneratedValue提供了主鍵的生成策略。
     * 稚机。@GeneratedValue注解有兩個屬性,分別是strategy和generator,
     * 其中g(shù)enerator屬性的值是一個字符串,默認(rèn)為"",其聲明了主鍵生成器的名稱
     * (對應(yīng)于同名的主鍵生成器@SequenceGenerator和@TableGenerator)幕帆。
     *  JPA為開發(fā)人員提供了四種主鍵生成策略,其被定義在枚舉類GenerationType中,
     *  包括GenerationType.TABLE,GenerationType.SEQUENCE,
     *  GenerationType.IDENTITY和GenerationType.AUTO。
     *  這里生成策略為自增長
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)  
    private Long id;
    @Column(nullable = false, unique = true)
    private String userName;
    @Column(nullable = false)
    private String passWord;
    @Column(nullable = false, unique = true)
    private String email;
    @Column(nullable = true, unique = true)
    private String nickName;
    @Column(nullable = false)
    private String regTime;

五赖条、創(chuàng)建Dao

方法的名稱要遵循 findBy + 屬性名(首字母大寫) + 查詢條件(首字母大寫 Is Equals)
findByNameLike(String name)
findByName(String name)
findByNameAndAge(String name, Integer age)
findByNameOrAddress(String name) 等...

@Repository
public interface UserRepository  extends JpaRepository<User,Long> {

    /**
     * 根據(jù)年紀(jì)查詢用戶
     * @param age
     * @return
     */
    User findByAge(Integer age);

    /**
     * 根據(jù)年紀(jì)和姓名查詢
     * @param name
     * @param age
     * @return
     */
    User findByNameAndAge(String name, Integer age);

    /**
     * 對于復(fù)雜查詢可以使用@Query 編寫sql
     * @param name
     * @return
     */
    @Query("from User u where u.name=:name")
    User findUser(@Param("name") String name);
}

該Dao成繼承了JpaRepository接口失乾,指定了需要操作的實體對象和實體對象的主鍵類型,通過查看JpaRepository接口源碼可以看到纬乍,里面已經(jīng)封裝了創(chuàng)建(save)碱茁、更新(save)、刪除(delete)仿贬、查詢(findAll纽竣、findOne)等基本操作的函數(shù),使用起來非常方便了茧泪,但是還是會存在一些復(fù)雜的sql蜓氨,spring-data-jpa還提供了一個非常方便的方式,通過實體屬性來命名方法队伟,它會根據(jù)命名來創(chuàng)建sql查詢相關(guān)數(shù)據(jù)语盈,對應(yīng)更加復(fù)雜的語句,還可以用直接寫sql來完成缰泡。

繼承了JpaRepository就相當(dāng)于有了下面的數(shù)據(jù)訪問操作方法:

@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
    List<T> findAll();
    List<T> findAll(Sort var1);
    List<T> findAll(Iterable<ID> var1);
    <S extends T> List<S> save(Iterable<S> var1);
    void flush();
    <S extends T> S saveAndFlush(S var1);
    void deleteInBatch(Iterable<T> var1);
    void deleteAllInBatch();
    T getOne(ID var1);
}

六、單元測試

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringdataApplicationTests {

    @Autowired
    private UserRepository userRepository;

    /**
     * 新增用戶
     * @throws Exception
     */
    @Test
    public void testAddUser() throws Exception {
        User user = new User();
        user.setName("zhangsan");
        user.setAge(12);
        userRepository.save(user);

        User user2 = new User();
        user2.setName("lishi");
        user2.setAge(22);
        userRepository.save(user2);
    }

    /**
     * 刪除用戶(根據(jù)對象刪除時代嗤,必須要有ID屬性)
     * @throws Exception
     */
    @Test
    public void testDelUser() throws Exception {
        User user = new User();
        user.setId(1L);
        user.setName("zhangsan");
        user.setAge(12);
        userRepository.delete(user);
    }

    /**
     * 修改用戶信息
     * @throws Exception
     */
    @Test
    public void testUpdUser() throws Exception {
        User user = new User();
        user.setId(2L);
        user.setName("zhangsan11");
        user.setAge(122);
        userRepository.save(user);
    }

    /**
     * 查詢用戶
     * @throws Exception
     */
    @Test
    public void testQueryUser() throws Exception {
        User user = userRepository.findByAge(22);
        System.out.println(user.getName());

        User user2 = userRepository.findByNameAndAge("lishi", 22);
        System.out.println(user2.getName());

        User user3 = userRepository.findUser("zhangsan11");
        System.out.println(user3.getName());
    }

    /**
     * 查詢所有用戶
     * @throws Exception
     */
    @Test
    public void testQueryUserList() throws Exception {
        List<User> list = userRepository.findAll();
        for (User user : list) {
            System.out.println(user.getName());
        }
    }
}

如果沒有表棘钞,將會自動生成。

七干毅、主要接口

CrudReposiroty : 繼承了Repository
Crud主要是添加了對數(shù)據(jù)的增刪改查的方法
PagingAndSortingRepository: 繼承了CrudRepository
JPARepository: 繼承了PagingAndSortingRepository接口
JpaSpecificationExecutor: 這個接口單獨存在宜猜,沒有繼承以上說的接口
主要提供了多條件查詢的支持,并且可以在查詢中添加分頁和排序硝逢。
因為這個接口單獨存在姨拥,因此需要配合以上說的接口使用,如:

/**
 * JpaSpecificationExecutor是單獨存在的渠鸽,需要配合這JpaRepository一起使用
 */
@Repository
public interface UserJpaSpecificationExecutor extends JpaSpecificationExecutor<User>, JpaRepository<User, Integer> {
}

八叫乌、JPA的查詢方法

基于@Query注解的查詢和更新:

/**
     * SQL nativeQuery的值是true 執(zhí)行的時候不用再轉(zhuǎn)化
     * @param name
     * @return
     */
    @Query(value = "SELECT * FROM table_user WHERE name = ?1", nativeQuery = true)
    List<User> findByUsernameSQL(String name);

基于HQL:

 /**
     * 基于HQL
     * @param name
     * @param id
     * @return
     */
    @Query("Update User set name = ?1 WHERE id = ?2")
    @Modifying
    int updateNameAndId(String name, Integer id);

在JPA中有三種方式可以進(jìn)行數(shù)據(jù)的查詢(1,方法命名查詢 2,@NamedQuery查詢 3,@Query查詢),
假設(shè)有一張表叫PERSON徽缚,字段:ID(INT),NAME(VARCHAR),AGE(INT),ADDRESS(VARCHAR).
實體類:id(integer),name(String),age(integer),address(String)

  1. 第一種:方法命名查詢

1) 使用findBy,And關(guān)鍵字

public interface PersonRepository extends Repository<Person, Integer> {
       /*
    * 通過地址進(jìn)行查詢憨奸,參數(shù)為address,
    * 相當(dāng)于JPQL:select p from Person p where p.address=?1
    * */
    List<Person> findByAddress(String address);
       /*
    * 通過地址和名字進(jìn)行查詢,參數(shù)為name,address
    * 相當(dāng)于JPQL:select p from Person p where p.name=?1 and address=?2
    * */
    Person findByNameAndAddress(String name,String address);
}

從代碼可以看出凿试,使用findBy,And這樣的關(guān)鍵字排宰,其中的findBy可以用find,getBy,query,read來進(jìn)行代替似芝。
而And就相當(dāng)于sql語句中的and。

2) 用關(guān)鍵字限制結(jié)果數(shù)量板甘,用top和first來實現(xiàn)

*
*查詢符合條件的前十條記錄
*/
List<Person> findFirst10ByName(String name)
/*
*查詢符合條件的前30條記錄
*/
List<Person> findTop30ByName(String name);
  1. 第二種:@NamedQuery查詢
    Spring Data JPA 支持@NameQuery來定義查詢方法党瓮,即一個名稱映射一個查詢語句(要在實體類上寫,不是接口里寫):
@Entity
@NamedQuery(name="Person.findByName",
query="select p from Person p where p.name=?1")
public class Person{
}

這樣子就重新定義了findByName這個方法了盐类。
如果要將多個方法都進(jìn)行重新定義寞奸,可以使用@NameQueries標(biāo)簽,示例如下:

@Entity
@NamedQueries({
@NamedQuery(name="Person.findByName",
query="select p from Person p where p.name=?1"),
@NamedQuery(name = "Person.withNameAndAddressNamedQuery",
query = "select p from Person p where p.name=?1 and address=?2")
})
public class Person{

}

這個時候,接口里定義的findByName方法就是上面的方法了傲醉,不再是方法命名查詢的方法了蝇闭。

  1. 第三種:@Query查詢
    Spring Data JPA 支持@Query來定義查詢方法,使用方法是將@Query寫在接口的方法上面:
public interface PersonRepository extends Repository<Person, Integer> {
     @Query("select p from Person p where p.name=?1 and p.address=?2")
    Person withNameAndAddressQuery(String name,String address);
   
}

這里的參數(shù)是根據(jù)索引號來進(jìn)行查詢的硬毕。
當(dāng)然我們也是可以根據(jù)名稱來進(jìn)行匹配呻引,然后進(jìn)行查詢的,示例如下:

public interface PersonRepository extends Repository<Person, Integer> {   
@Query("select p from Person p where p.name= :name and p.address= :address")
Person withNameAndAddressQuery(@Param("name")String name,@Param("address")String address);
}

Spring Data JPA支持使用@Modifying和@Query注解組合來進(jìn)行更新查詢吐咳,示例如下:

public interface PersonRepository extends Repository<Person, Integer> {

@Modifying
@Transcational
@Query("update Person p set p.name=?1 ")
int setName(String name);
}

int表示的是更新語句所影響的行數(shù)逻悠。

  1. 排序查詢
userRepository.findAll(new Sort(new Sort.Order(Sort.Direction.ASC,"id")));
  1. 分頁查詢
//分頁查詢?nèi)?返回封裝了分頁信息,注意: 0為第一頁韭脊,1為第2頁
Page<User> pageInfo = userRepository.findAll(PageRequest.of(0, 3, Sort.Direction.ASC, "id"));
log.info("總頁數(shù)" + pageInfo.getTotalPages());
log.info("頁大小" + pageInfo.getSize());
log.info("當(dāng)前頁" + pageInfo.getPageable().getPageNumber());
log.info("總記錄數(shù)" + pageInfo.getTotalElements());
//內(nèi)容
List<User> userList = pageInfo.getContent();

  1. example查詢
User user = new User();
user.setUsername("admin");
Example<User> example = Example.of(user);
List<User> list = userRepository.findAll(example);
log.info(list);
  1. getOne方法
    需添加配置:
#延遲加載
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

運行:

# 只可讀取存在的數(shù)據(jù)童谒,不存在拋異常
  User user=userRepository.getOne(1L);
  1. findOne方法
User user=new User();
user.setId(3L);
Example example=Example.of(user);
Optional<User> optionalUser=userRepository.findOne(example);
log.info(optionalUser.get().getUsername());

說明:如果不存在,會報:No value present異常

User user = new User();
user.setId(3L);
Example example = Example.of(user);
Optional<User> optionalUser = userRepository.findOne(example);
//判斷是否存在
if (optionalUser.isPresent()) {
    log.info(optionalUser.get().getUsername());
}
或:
//存在即返回, 無則提供默認(rèn)值
User user = new User();
user.setId(id);
Example<User> userExample = Example.of(user);
return userRepository.findOne(userExample).orElse(null); 

在查詢時沪羔,通常需要同時根據(jù)多個屬性進(jìn)行查詢饥伊,且查詢的條件也格式各樣(大于某個值、在某個范圍等等)蔫饰,Spring Data JPA 為此提供了一些表達(dá)條件查詢的關(guān)鍵字琅豆,大致如下:
And --- 等價于SQL 中的and 關(guān)鍵字,比如findByUsernameAndPassword(String user, Striang pwd)篓吁;
Or --- 等價于SQL 中的or 關(guān)鍵字茫因,比如findByUsernameOrAddress(String user, String addr);
Between --- 等價于SQL 中的between 關(guān)鍵字杖剪,比如findBySalaryBetween(int max, int min)冻押;
LessThan --- 等價于SQL 中的"<",比如findBySalaryLessThan(int max)盛嘿;
lGreaterThan --- 等價于SQL 中的">"洛巢,比如findBySalaryGreaterThan(int min);
IsNull --- 等價于SQL 中的"is null"次兆,比如findByUsernameIsNull()狼渊;
IsNotNull --- 等價于SQL 中的"is not null",比如findByUsernameIsNotNull();
NotNull --- 與IsNotNull 等價狈邑;
Like --- 等價于SQL 中的"like"城须,比如findByUsernameLike(String user);
NotLike --- 等價于SQL 中的"not like"米苹,比如findByUsernameNotLike(String user)糕伐;
OrderBy --- 等價于SQL 中的"order by",比如findByUsernameOrderBySalaryAsc(String user)蘸嘶;
Not --- 等價于SQL 中的"良瞧! =",比如findByUsernameNot(String user)训唱;
In --- 等價于SQL 中的"in"褥蚯,比如findByUsernameIn(Collection<String> userList) ,方法的參數(shù)可以
是Collection 類型况增,也可以是數(shù)組或者不定長參數(shù)赞庶;
NotIn --- 等價于SQL 中的"not in",比如findByUsernameNotIn(Collection<String> userList) 澳骤,方法的參數(shù)可以是Collection 類型歧强,也可以是數(shù)組或者不定長參數(shù);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末为肮,一起剝皮案震驚了整個濱河市摊册,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌颊艳,老刑警劉巖茅特,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異棋枕,居然都是意外死亡温治,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門戒悠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人舟山,你說我怎么就攤上這事绸狐。” “怎么了累盗?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵寒矿,是天一觀的道長。 經(jīng)常有香客問我若债,道長符相,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮啊终,結(jié)果婚禮上镜豹,老公的妹妹穿的比我還像新娘。我一直安慰自己蓝牲,他們只是感情好趟脂,可當(dāng)我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著例衍,像睡著了一般昔期。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上佛玄,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天硼一,我揣著相機與錄音,去河邊找鬼梦抢。 笑死般贼,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惑申。 我是一名探鬼主播具伍,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼圈驼!你這毒婦竟也來了人芽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绩脆,失蹤者是張志新(化名)和其女友劉穎萤厅,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體靴迫,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡惕味,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了玉锌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片名挥。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖主守,靈堂內(nèi)的尸體忽然破棺而出禀倔,到底是詐尸還是另有隱情,我是刑警寧澤参淫,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布救湖,位于F島的核電站,受9級特大地震影響涎才,放射性物質(zhì)發(fā)生泄漏鞋既。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望邑闺。 院中可真熱鬧跌前,春花似錦、人聲如沸检吆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹭沛。三九已至臂寝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間摊灭,已是汗流浹背咆贬。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留帚呼,地道東北人掏缎。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像煤杀,于是被迫代替她去往敵國和親眷蜈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,960評論 2 355

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