Spring Boot學習筆記(四):Spring Boot 數(shù)據(jù)訪問

Spring Data項目是Spring用來解決數(shù)據(jù)訪問問題的一攬子解決方案。

全部章節(jié)傳送門:
Spring Boot學習筆記(一):Spring Boot 入門基礎(chǔ)
Spring Boot學習筆記(二):Spring Boot 運行原理
Spring Boot學習筆記(三):Spring Boot Web開發(fā)
Spring Boot學習筆記(四):Spring Boot 數(shù)據(jù)訪問
Spring Boot學習筆記(五):Spring Boot 企業(yè)級開發(fā)
Spring Boot學習筆記(六):Spring Boot 應(yīng)用監(jiān)控

Spring Data JPA

Spring Data JPA是Spring Data的一個子項目婚脱,它提供了一套簡化JPA開發(fā)的框架,用來簡化數(shù)據(jù)庫訪問增热。同時提供了很多除了CRUD之外的功能,如分頁盅粪、排序钓葫、復雜查詢等等。

準備環(huán)境

創(chuàng)建數(shù)據(jù)表

在MySQL數(shù)據(jù)庫中建立一個數(shù)據(jù)表t_person票顾,用來進行后面的測試础浮,并在里面隨便添加幾條數(shù)據(jù)帆调。

create table t_person  (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name varchar(10),
    age INT,
    address VARCHAR(20)
) CHARACTER SET UTF8;

建立項目

建立一個springboot項目,在pom.xml中添加如下依賴豆同。其中g(shù)uava是一個工具包番刊。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>18.0</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

配置基本屬性

在application.properties里面配置數(shù)據(jù)源和JPA相關(guān)屬性。

# 數(shù)據(jù)庫相關(guān)
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springstudy
spring.datasource.username=spring
spring.datasource.password=spring
spring.datasource.dirverClassName=com.mysql.jdbc.Driver
# 根據(jù)實體類自動維護數(shù)據(jù)庫表結(jié)構(gòu)的功能
spring.jpa.hibernate.ddl-auto=none
# 設(shè)置hibernate操作的時候在控制臺顯示真實的SQL語句
spring.jpa.show-sql=true
# 讓控制器輸出的json字符串格式更美觀
spring.jackson.serialization.indent_output=true

其中影锈,spring.jpa.hibernate.ddl-auto 提供根據(jù)實體類自動維護數(shù)據(jù)庫表結(jié)構(gòu)的功能芹务,可選值包括:

  • create----每次運行該程序,沒有表格會新建表格鸭廷,表內(nèi)有數(shù)據(jù)會清空枣抱。
  • create-drop----每次程序結(jié)束的時候會清空表。
  • update----每次運行程序辆床,沒有表格會新建表格佳晶,表內(nèi)有數(shù)據(jù)不會清空,只會更新讼载。
  • validate----運行程序會校驗數(shù)據(jù)與數(shù)據(jù)庫的字段類型是否相同轿秧,不同會報錯。
  • none----不采取任何措施咨堤。

定義映射實體類

創(chuàng)建實體類Person菇篡,將數(shù)據(jù)表的字段映射過來。

其中一喘,@Entity注解表明這個類是一個實體驱还,任何Hibernate映射對象都要有這個注解,@Table注解用來映射表名津滞,@Column注解用來映射屬性名和字段名铝侵,不注解的時候可以自動映射灼伤,比如將name映射為NAME触徐,將testName映射為TEST_NAME。

package com.wyk.datademo;

import javax.persistence.*;

@Entity
@Table(name = "t_person") //表名
public class Person {
    @Id //映射為數(shù)據(jù)庫主鍵
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 生成方式為自增
    private Long id;
    private String name;
    private Integer age;
    private String address;

    public Person() {}

    public Person(Long id, String name, Integer age, String address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

定義數(shù)據(jù)訪問層

使用Spring Data JPA建立數(shù)據(jù)訪問層需要定義一個繼承JapRepository的接口狐赡。

package com.wyk.datademo;

import org.springframework.data.jpa.repository.JpaRepository;

public class PersonRespositary extends JpaRepository {
    ...
}

JpaRepository接口存在如下數(shù)據(jù)訪問操作方法撞鹉。

package org.springframework.data.jpa.repository;

import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    List<T> findAll();

    List<T> findAll(Sort var1);

    List<T> findAllById(Iterable<ID> var1);

    <S extends T> List<S> saveAll(Iterable<S> var1);

    void flush();

    <S extends T> S saveAndFlush(S var1);

    void deleteInBatch(Iterable<T> var1);

    void deleteAllInBatch();

    T getOne(ID var1);

    <S extends T> List<S> findAll(Example<S> var1);

    <S extends T> List<S> findAll(Example<S> var1, Sort var2);
}

配置使用 Spring Data JPA

在Spring中,可以通過@EnableJpaRepositories注解來開啟Spring Data JPA的支持颖侄,通過接收的value參數(shù)來掃描數(shù)據(jù)訪問層所在包下的數(shù)據(jù)訪問接口定義鸟雏。

package com.wyk.datademo;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

import javax.persistence.EntityManagerFactory;

@Configuration
@EnableJpaRepositories("com.wyk.datademo")
public class JpaConfigurattion {
    public EntityManagerFactory entityManagerFactory() {
        ...
    }
    ...
}

在Sring Boot中,會進行自動配置览祖,不需要添加如上配置代碼孝鹊。

定義查詢方法

根據(jù)屬性名查詢

Spring Data JPA支持通過定義在Repository接口中的方法名來定義查詢,而方法名是根據(jù)實體類的屬性名來確定展蒂。

根據(jù)屬性名來定義查詢方法又活。

public interface PersonRepository extends JpaRepository<Person, Long> {
    /**
     * 通過名字查詢
     * 相當于 select p from Person p where p.name=?1
     * @param name
     * @return
     */
    List<Person> findByName(String name);

    /**
     * 通過名字模糊查詢
     * 相當于select p from Person p where p.name like ?1
     * @param name
     * @return
     */
    List<Person> findByNameLike(String name);

    /**
     * 通過名字和地址查詢
     * 相當于 select p from Perosn p where p.name=?1 and p.address=?2
     * @param name
     * @param address
     * @return
     */
    List<Person> findByNameAndAddress(String name, String address);
}

還可以通過top和first等關(guān)鍵字來限制結(jié)果數(shù)量苔咪。

```java
/**
 * 查詢符合條件的前10條數(shù)據(jù)
 * @param name
 * @return
 */
List<Person> findFirst10ByName(String name);

/**
 * 查詢符合條件的奇拿30條數(shù)據(jù)
 * @param name
 * @return
 */
List<Person> findTop30ByName(String name);

使用JPA的NamedQuery查詢

Spring Data JPA 支持用JPA的NamedQuery來定義查詢方法,即一個名稱映射一個查詢語句柳骄。需要在實體類上添加@NamedQuery注解团赏。

@Entity
@NamedQuery(name="Person.withNameAndAddressNamedQuery",
    query = "select p from Person p where p.name = ?1 and address=?2")
@Table(name = "t_person") //表名
public class Person {
    ...
}

查詢使用如下語句。

/**
 * 使用NamdeQuery里定義的查詢語句
 * @param name
 * @return
 */
Person withNameAndAddressQuery(String name, String address);

使用@Query查詢

Spring Data JPA 還支持用@Query注解在接口的方法上實現(xiàn)查詢耐薯,可以根據(jù)參數(shù)索引舔清。

@Query("select p from Person p where p.name=?1 and p.address=?2")
Person withNameAndAddressQuery(String name, String address);

還可以使用參數(shù)的名稱來匹配查詢參數(shù)。

@Query("select p from Person p where p.address= :address")
List<Person> findByAddress(@Param("address")String address);

Spring Data JPA 支持@Modifying和@Query注解組合來事件更新查詢曲初。

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

分頁與排序

Spring Data JPA 也對排序和分頁提供了支持体谒。

/**
 * 查詢結(jié)果排序
 * @param name
 * @param sort
 * @return
 */
List<Person> findByName(String name, Sort sort);

/**
 * 查詢結(jié)果分頁
 * @param name
 * @param pageable
 * @return
 */
Page<Person> findByName(String name, Pageable pageable);

使用排序:

List<Person> people = personRepository.findByName("haha", new Sort(Sort.Direction.ASC, "age"));

使用分頁:

Page<Person> people2 = personRepository.findByName("haha", PageRequest.of(0, 10));

Page接口可有獲取當前頁面記錄、總頁數(shù)臼婆、總記錄數(shù)等营密。

Specification

JPA 提供了基于準則查詢的方式,即Criteria查詢目锭,可以用來進行復雜的動態(tài)查詢评汰。Spring Data JPA 提供了一個Specification接口,其中定義了一個toPredicate方法用來構(gòu)造查詢條件痢虹。

定義一個Criterial查詢被去。其中Root用來獲取需要查詢的屬性,通過CriteriaBuilder構(gòu)造查詢條件(例子中是來自北京的人).

public class CustomerSpecs {
    public static Specification<Person> personFromBeijing() {
        return new Specification<Person>() {
            @Override
            public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> criteriaQuery,
                                         CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.equal(root.get("address"), "北京");
            }
        };
    }
}

在接口類上需要實現(xiàn)JpaSpecificationExecutor接口奖唯。

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

使用的時候需要靜態(tài)導入惨缆。

import static com.wyk.datademo.CustomerSpecs.*;

注入personRepository的Bean后可以調(diào)用方法。

List<Person> people = personRepository.findAll(perosnFromBeijing());

添加控制器

將PersonRepository注入到控制器丰捷。

package com.wyk.datademo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class DataController {

    //Spring Data JPA 已自動為你注冊bean坯墨,所以可自動注入
    @Autowired
    PersonRepository personRepository;

    /**
     * 保存
     * @param name
     * @param address
     * @param age
     * @return
     */
    @RequestMapping("/save")
    public Person save(String name, String address, Integer age) {
        // save 支持批量保存
        Person p = personRepository.save(new Person(null, name, age, address));
        return p;
    }

    /**
     * 測試findByAddress
     * @param address
     * @return
     */
    @RequestMapping("/q1")
    public List<Person> q1(String name) {
        List<Person> people = personRepository.findByName(name);
        return people;
    }

    /**
     * 測試findByNameAndAddress
     * @param name
     * @param address
     * @return
     */
    @RequestMapping("/q2")
    public Person q2(String name, String address) {
        Person people = personRepository.findByNameAndAddress(name, address);
        return people;
    }

    /**
     * 測試withNameAndAddressQuery
     * @param name
     * @param address
     * @return
     */
    @RequestMapping("/q3")
    public Person q3(String name, String address) {
        Person people = personRepository.withNameAndAddressQuery(name, address);
        return people;
    }

    /**
     * 測試withNameAndAddressNamedQuery
     * @param name
     * @param address
     * @return
     */
    @RequestMapping("/q4")
    public Person q4(String name, String address) {
        Person people = personRepository.withNameAndAddressNamedQuery(name, address);
        return people;
    }

    /**
     * 測試排序
     * @return
     */
    @RequestMapping("/sort")
    public List<Person> sort() {
        List<Person> people = personRepository.findAll(new Sort(Sort.Direction.ASC, "age"));
        return people;
    }

    /**
     * 測試分頁
     * @return
     */
    @RequestMapping("/page")
    public Page<Person> page() {
        Page<Person> pagePeople = personRepository.findAll(PageRequest.of(1, 2));
        return pagePeople;
    }
}

查看運行結(jié)果

運行項目,依次查看結(jié)果:

保存實體(http://localhost:8080/save?name=ss&address=Shanghai&age=25):

springboot-jpa-save.png

根據(jù)屬性查詢(http://localhost:8080/q1?name=xiaoming):

springboot-jpa-q1.png

根據(jù)多條屬性查詢(http://localhost:8080/q2?name=xiaoming&address=Beijing):

springboot-jpa-q2.png

根據(jù)@Query注解查詢(http://localhost:8080/q3?name=xiaoming&address=Beijing):

springboot-jpa-q3.png

根據(jù)NamedQuery查詢(http://localhost:8080/q4?name=xiaoming&address=Beijing):

springboot-jpa-q4.png

查詢結(jié)果排序(http://localhost:8080/sort):

springboot-jpa-sort.png

查詢結(jié)果分頁(http://localhost:8080/sort):

springboot-jpa-page.png

自定義Repository的實現(xiàn)

我們可以通過Spring Data JPA的JpaRepository封裝自己的數(shù)據(jù)庫操作病往,提供給Repository接口使用捣染。

定義Specification

首先需要定義Specification,本部分定制一個自動模糊查詢:當值為字符型時使用like查詢停巷,其余類型使用等于查詢耍攘,沒有值就查詢?nèi)俊?/p>

package com.wyk.datademo;

import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import static com.google.common.collect.Iterables.toArray;

public class CustomerSpecs {
    /**
     * 定義一個返回值為Specification的方法byAuto
     * @param entityManager
     * @param example
     * @param <T>
     * @return
     */
    public static <T> Specification<T> byAuto(final EntityManager entityManager,
                                              final T example) {
        // 獲得當前實體類對象的類型
        final Class<T> type = (Class<T>)example.getClass();
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery,
                                         CriteriaBuilder criteriaBuilder) {
                // 新建Predicate列表存儲構(gòu)造的查詢條件
                List<Predicate> predicates = new ArrayList<>();
                // 獲取實體類的entityType,我們可以從中獲得實體類的屬性
                EntityType<T> entity = entityManager.getMetamodel().entity(type);
                //對實體類的屬性進行循環(huán)
                for(Attribute<T, ?> attr : entity.getDeclaredAttributes()) {
                    // 獲取實體類對象某一屬性的值
                    Object attrValue =  getValue(example, attr);
                    if(attrValue != null) {
                        // 當前屬性為字符類型的時候
                        if(attr.getJavaType() == String.class) {
                            // 當前字符不為空的情況下
                            if(!StringUtils.isEmpty(attrValue)) {
                                // 構(gòu)造當前屬性like查詢條件畔勤,并添加條件列表
                                predicates.add(criteriaBuilder.like(root.get(attribute(entity, attr.getName(),
                                        String.class)), pattern((String) attrValue)));
                            } else {
                                // 構(gòu)造屬性和屬性值equal查詢條件蕾各,并添加到條件列表中
                                predicates.add(criteriaBuilder.equal(root.get(attribute(entity,
                                        attr.getName(), attrValue.getClass())), attrValue));
                            }

                        }
                    }
                }
                //將條件列表轉(zhuǎn)換成Predicate
                return predicates.isEmpty() ? criteriaBuilder.conjunction() :
                        criteriaBuilder.and(toArray(predicates, Predicate.class));
            }

            /**
             * 通過反射獲取實體類對象對應(yīng)屬性的屬性值
             * @param example
             * @param attr
             * @param <T>
             * @return
             */
            private <T> Object getValue(T example, Attribute<T, ?> attr) {
                return ReflectionUtils.getField((Field) attr.getJavaMember(), example);
            }

            /**
             * 獲取實體類當前屬性的SingularAttribute
             * @param entity
             * @param fieldName
             * @param fieldClass
             * @param <E>
             * @param <T>
             * @return
             */
            private <E, T> SingularAttribute<T, E> attribute(EntityType<T> entity,
                                                            String fieldName, Class<E> fieldClass) {
                return entity.getDeclaredSingularAttribute(fieldName, fieldClass);
            }
        };
    }

    /**
     * 構(gòu)造like的查詢模式
     * @param str
     * @return
     */
    static private String pattern(String str) {
        return "%" + str + "%";
    }
}

定義Repository接口

定義一個繼承JpaRepository的接口,使它具備JpaRepository接口的所有方法庆揪,還繼承了JpaSpecificationExecutor式曲,具備使用Specification的能力。

package com.wyk.datademo;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;

import java.io.Serializable;

@NoRepositoryBean //表示當前接口不是領(lǐng)域類的接口
public interface CustomRepository<T, ID extends Serializable> extends
        JpaRepository<T, ID>, JpaSpecificationExecutor<T> {

    Page<T> findByAuto(T example, Pageable pageable);
}

定義接口實現(xiàn)

定義一個實現(xiàn)前面接口的類缸榛,并繼承SimpleJpaRepository吝羞,讓我們可以使用SimpleJpaRepository中的方法始鱼。

package com.wyk.datademo;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;

import javax.persistence.EntityManager;
import java.io.Serializable;

import static com.wyk.datademo.CustomerSpecs.byAuto;

public class CustomRepositoryImpl <T, ID extends Serializable> extends
        SimpleJpaRepository<T, ID> implements CustomRepository<T, ID> {

    private final EntityManager entityManager;

    public CustomRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
        super(domainClass, entityManager);
        this.entityManager = entityManager;
    }

    /**
     * 實現(xiàn)用byAuto的條件查詢,并提供分頁查詢
     * @param example
     * @param pageable
     * @return
     */
    @Override
    public Page<T> findByAuto(T example, Pageable pageable) {
        return findAll(byAuto(entityManager, example), pageable);
    }
}

定義repositoryFactoryBean

自定義repositoryFactoryBean擴展JpaRepositoryFactoryBean脆贵,可以從獲得一個RepositoryFactory医清。

package com.wyk.datademo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;

import javax.persistence.EntityManager;
import java.io.Serializable;

public class CustomRepositaryFactoryBean<T extends JpaRepository<S, ID>, S, ID
    extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> {

    public CustomRepositaryFactoryBean(Class<? extends T> repositoryInterface) {
        super(repositoryInterface);
    }

    @Override
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new CustomRepositoryFactory(entityManager);
    }

    private static class CustomRepositoryFactory<S, ID extends Serializable>
        extends JpaRepositoryFactory {

        public CustomRepositoryFactory(EntityManager entityManager) {
            super(entityManager);
        }

        @Override
        @SuppressWarnings({"unchecked"})
        protected SimpleJpaRepository<?, ?> getTargetRepository(
                RepositoryInformation information, EntityManager entityManager) {// 獲得當前自定義類的實現(xiàn)
            return new CustomRepositoryImpl<S, ID>((Class<S>) information.getDomainType(), entityManager);

        }
        /*
        @Override
        @SuppressWarnings({"unchecked"})
        protected <T, ID extends Serializable> SimpleJpaRepository<?, ?>
        getTargetRepository (RepositoryInformation information,
                             EntityManager entityManager) {
            return new CustomRepositoryImpl<T, ID>((Class<T>) information.getDomainType(),
                    entityManager);
        }
        */

        @Override
        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {

            return CustomRepositoryImpl.class;
        }
    }
}

使用自定義倉庫

讓實體類的Repository繼承自定義的Repository接口旗扑,即可使用自定義Repository中實現(xiàn)的功能祥诽。

public interface PersonRepository extends CustomRepository<Person, Long>,
        JpaSpecificationExecutor<Person> {
    ...
}

在控制器中添加測試方法保礼。

/**
 * 測試自定義倉庫
 * @param person
 * @return
 */
@RequestMapping("/auto")
public Page<Person> auto(Person person) {
Page<Person> pagePeople = personRepository.findByAuto(person, PageRequest.of(0, 10));
return pagePeople;
}

在運行類上使用@EnableJpaRepositories讓自定義的Repoisitory生效诵竭。

@SpringBootApplication
@EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositaryFactoryBean.class)
public class DatademoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DatademoApplication.class, args);
    }

}

查看效果

運行程序醒叁,訪問http://localhost:8080/auto, 無查詢條件回窘,查看結(jié)果冯挎。

springboot-jpa-auto1.png

訪問http://localhost:8080/auto?address=h泉瞻,進行模糊查詢系吭。

springboot-jpa-auto2.png

Spring Data REST

Spring Data REST是基于Spring Data的repository之上五嫂,可以將repository自動輸出為REST資源。

配置Spring Data REST

Spring Data REST的配置是定義在RepositoryRestMvcConfiguration配置類中肯尺,我們可以通過繼承此類或者直接在自己的配置類上@Import此配置類沃缘。

繼承方式:

@Configuration
public class MyRepositoryRestMvcConfiguration extends RepositoryRestMvcConfiguration {
    @Override
    public RepositoryRestConfiguraiton config() {
        return super.config();
    }

    //其它可重寫以config開頭的方法
    ...
}

導入方式:

@Configuration
@Import(RepositoryRestMvcConfiguration.class) 
public class AppConfig {
    ...
}

Spring Boot對Spring Data REST的自動配置放置在rest包中.通過SpringBootRestConfiguration類的源碼我們可以得出,Spring Boot已經(jīng)為我們自動配置了RepositoryRestConfiguration则吟,所以在Spring boot中使用Spring Data REST只需引入spring-boot-starter-data-rest的依賴槐臀,無須任何配置即可使用

Spring boot通過在application.properties中配置以spring.data.rest為前綴的屬性來配置RepositoryRestConfiguration氓仲。

Spring Data REST實戰(zhàn)

新建SpringBoot項目水慨,與上例類似,依賴在前面的基礎(chǔ)上增加REST(spring-boot-starter-data-rest)敬扛。application.properties的配置信息與前面一樣晰洒。

添加同樣的實體類Person.java,并定義實體類的Repository啥箭。其中谍珊,在Repository中的方法上添加注解@RestResource可以將方法暴漏為REST源。

package com.wyk.datarestdemo.repository;

import com.wyk.datarestdemo.bean.Person;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RestResource;

public interface PersonRepository extends JpaRepository<Person, Long> {
    @RestResource(path="nameStartsWith", rel="nameStartsWith")
    Person findByNameStartsWith(String name);
}

為了方便測試捉蚤,我們使用一個工具Postman抬驴,可以在官網(wǎng)直接下載。

運行程序缆巧,打開Postman,在其中使用GET訪問http://localhost:8080/persons/1, 收到如下返回豌拙。

springboot-rest-1.png

使用GET訪問地址 http://localhost:8080/persons/search/nameStartsWith?name=Li, 用來測試方法陕悬。

springboot-rest-2.png

Spring Data Rest 還支持分頁和排序,以及更新按傅、保存捉超、刪除等多個操作胧卤,這里不再展示。

Spring Data Rest定制

定制根路徑

前面提到相關(guān)配置都在application.properties中配置以spring.data.rest為前綴的屬性來配置拼岳。默認訪問路徑是根路徑枝誊,如果想修改,可以進行如下配置惜纸。

spring.data.rest.base-path=/api

定制節(jié)點路徑

在上面的例子叶撒,我們使用 http://localhost:8080/persons 訪問,這時Spring Data REST的默認規(guī)則耐版,使用實體類加s形成路徑祠够。如果想對映射名稱進行修改,需要在實體類Repository上使用@RepositoryRestResource 注解的path屬性進行修改粪牲。

@RepsositoryRestResource(path="people")
public interface PersonRepository extends JpaRepository<Person, Long> {
    @RestResource(path="nameStartsWith", rel="nameStartsWith")
    Person findByNameStartsWith(String name);
}

這樣訪問地址就變成了 http://localhost:8080/api/people 古瓤。

聲明式事務(wù)

Spring事務(wù)機制

Spring的事務(wù)機制是用統(tǒng)一的機制來處理不同數(shù)據(jù)訪問技術(shù)的事務(wù)處理。Spring的事務(wù)機制提供了一個PlatformTransactionManager接口腺阳,不同的數(shù)據(jù)訪問技術(shù)的事務(wù)使用不同的接口實現(xiàn)落君。

數(shù)據(jù)庫訪問技術(shù) 實現(xiàn)
JDBC DataSourceTransactionManager
JPA JpaTransactionManager
Hibernate HibernateTransactionManager
JDO JdoTransactionManager
分布式事務(wù) JtaTransactionManager

注解事務(wù)行為

Spring支持聲明式事務(wù)。即使用注解來選擇需要使用事務(wù)的方法亭引。它使用@Transactional注解在方法上表明該方法需要事務(wù)支持叽奥。如果@Transactional注解使用在類上,則此類的所有public方法都是開啟事務(wù)的痛侍。

Spring提供了一個@EnableTransactionManagement注解在配置類上開啟聲明式事務(wù)支持朝氓。使用方式:

@Configuration
@EnableTransactionManagement
public class AppConfig {
    ...
}

@Transactional的屬性如下表。

參數(shù)名稱 功能描述 默認值
readOnly 該屬性用于設(shè)置當前事務(wù)是否為只讀事務(wù) false
rollbackFor 該屬性用于設(shè)置需要進行回滾的異常類數(shù)組主届,當方法中拋出指定異常數(shù)組中的異常時赵哲,則進行事務(wù)回滾。 Throwble的子類
noRollbackFor 該屬性用于設(shè)置不需要進行回滾的異常類數(shù)組君丁,當方法中拋出指定異常數(shù)組中的異常時枫夺,不進行事務(wù)回滾。 Throwble的子類
propagation 該屬性用于設(shè)置事務(wù)的傳播行為绘闷。 REQUIRED
isolation 該屬性用于設(shè)置底層數(shù)據(jù)庫的事務(wù)隔離級別橡庞,事務(wù)隔離級別用于處理多事務(wù)并發(fā)的情況,通常使用數(shù)據(jù)庫的默認隔離級別即可印蔗,基本不需要進行設(shè)置扒最。 DEFAULT
timeout 該屬性用于設(shè)置事務(wù)的超時秒數(shù) TIMEOUT_DEFAULT

SpringBoot事務(wù)支持

Spring Data JPA對所有默認的方法都開啟了事務(wù)支持,且查詢類事務(wù)默認啟用readOnly=true屬性华嘹。

Spring Boot會自動配置事務(wù)管理器吧趣,且會自動開啟注解事務(wù)的支持。

使用和前面相同的例子,創(chuàng)建一個實體類Person和接口PersonRepository强挫。

添加業(yè)務(wù)服務(wù)接口岔霸。

package com.wyk.datarestdemo.service;

import com.wyk.datarestdemo.bean.Person;

public interface DemoService {
    public Person savePersonWithRollBack(Person person);
    public Person savePersonWithoutRollBack(Person person);
}

添加業(yè)務(wù)服務(wù)實現(xiàn)。

package com.wyk.datarestdemo.service.impl;

import com.wyk.datarestdemo.bean.Person;
import com.wyk.datarestdemo.repository.PersonRepository;
import com.wyk.datarestdemo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class DemoServiceImpl  implements DemoService {
    @Autowired
    PersonRepository personRepository;

    @Transactional(rollbackFor={IllegalArgumentException.class})
    public Person savePersonWithRollBack(Person person) {
        Person p = personRepository.save(person);

        if(person.getName().equals("wyk")) {
            throw new IllegalArgumentException("wyk已存在俯渤,數(shù)據(jù)將回滾");
        }
        return p;
    }

    @Transactional(noRollbackFor = {IllegalArgumentException.class})
    public Person savePersonWithoutRollBack(Person person) {
        Person p = personRepository.save(person);

        if(person.getName().equals("wyk")) {
            throw new IllegalArgumentException("wyk雖已存在呆细,數(shù)據(jù)不會回滾");
        }
        return p;
    }
}

添加控制器。

package com.wyk.datarestdemo.controller;

import com.wyk.datarestdemo.bean.Person;
import com.wyk.datarestdemo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {
    @Autowired
    DemoService demoService;

    @RequestMapping("/rollback")
    public Person rollback(Person person) {
        return demoService.savePersonWithRollBack(person);
    }

    @RequestMapping("/norollback")
    public Person noRollback(Person person) {
        return demoService.savePersonWithoutRollBack(person);
    }
}

運行程序八匠,訪問 http://localhost:8080/rollback?name=wyk&age=29 絮爷,這時程序拋出異常。

java.lang.IllegalArgumentException: wyk已存在臀叙,數(shù)據(jù)將回滾
    at com.wyk.datarestdemo.service.impl.DemoServiceImpl.savePersonWithRollBack(DemoServiceImpl.java:20) ~[classes/:na]
    at com.wyk.datarestdemo.service.impl.DemoServiceImpl$$FastClassBySpringCGLIB$$2ef6f418.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    ...

查看數(shù)據(jù)庫略水。

mysql> select * from t_person;
+----+----------+------+----------+
| id | name     | age  | address  |
+----+----------+------+----------+
|  1 | xiaoming |   22 | Beijing  |
|  2 | xiaohong |   21 | Beijing  |
|  3 | Peter    |   18 | New York |
|  4 | Jingjing |   18 | Hengshui |
|  5 | Lily     |   28 | Tianjin  |
|  6 | ss       |   25 | Shanghai |
+----+----------+------+----------+
6 rows in set (0.00 sec)

并沒有插入成功。

改為訪問 http://localhost:8080/norollback?name=wyk&age=29 劝萤,這時程序同樣拋出異常渊涝。

java.lang.IllegalArgumentException: wyk雖已存在,數(shù)據(jù)不會回滾
    at com.wyk.datarestdemo.service.impl.DemoServiceImpl.savePersonWithoutRollBack(DemoServiceImpl.java:30) ~[classes/:na]
    at com.wyk.datarestdemo.service.impl.DemoServiceImpl$$FastClassBySpringCGLIB$$2ef6f418.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    '''

查看數(shù)據(jù)庫床嫌,發(fā)現(xiàn)語句已經(jīng)插入成功跨释。

mysql> select * from t_person;
+----+----------+------+----------+
| id | name     | age  | address  |
+----+----------+------+----------+
|  1 | xiaoming |   22 | Beijing  |
|  2 | xiaohong |   21 | Beijing  |
|  3 | Peter    |   18 | New York |
|  4 | Jingjing |   18 | Hengshui |
|  5 | Lily     |   28 | Tianjin  |
|  6 | ss       |   25 | Shanghai |
| 10 | wyk      |   29 | NULL     |
+----+----------+------+----------+
7 rows in set (0.00 sec)

Spring 數(shù)據(jù)緩存

緩存是實際工作中非經(jīng)常常使用的一種提高性能的方法, 我們會在很多場景下來使用緩存。

Spring 緩存支持

Spring 定義了 org.springframework.cache.CacheMananger 和 org.springframework.cache.Cache 接口用來統(tǒng)一不同的緩存技術(shù)厌处。其中鳖谈,CacheManager 是 Spring 提供的各種緩存技術(shù)的抽象接口, Cache 接口包含緩存的各種操作(一般不直接使用)阔涉。

Spring 支持的 CacheManager

針對不同的緩存技術(shù)缆娃,需要實現(xiàn)不同的 CacheManger,Spring 定義了多個 CacheManager 實現(xiàn)瑰排。

CacheManager 描述
SimpleCacheManager 使用簡單的 Collection 來存儲緩存贯要,主要用來測試用途
ConcurrentMapCacheManager 使用 ConcurrentMap 來存儲緩存
NoOpCacheManager 僅測試用途,不會實際存儲緩存
EhCacheCacheManager 使用 EhCache 作為緩存技術(shù)
GuavaCacheManger 使用 Google Guava 的 GuavaCache 作為緩存技術(shù)
HazelcastCacheManager 使用 Hazelcast 作為緩存
JCacheCacheManager 使用 JCache 標準的實現(xiàn)作為緩存技術(shù)
RedisCacheManager 使用Redis作為緩存技術(shù)

在我們使用任意一個實現(xiàn)的 CacheManager 的時候椭住,需注冊實現(xiàn) CacheManager 的 Bean崇渗。

@Bean
public EhCacheCacheManager cacheManager(CacheManager ehCacheCacheManager) {
    return new EhCacheCacheManager(ehCacheCacheManager);
}

聲明式緩存注解

Spring 提供了4個注解來聲明緩存規(guī)則。

注解 作用
@Cacheable 方法執(zhí)行前京郑,先從緩存中讀取數(shù)據(jù)宅广,如果緩存沒有找到數(shù)據(jù),再調(diào)用方法獲取數(shù)據(jù)些举,然后把數(shù)據(jù)添加到緩存中
@CachePut 調(diào)用方法時會自動把方法返回的相應(yīng)數(shù)據(jù)放入緩存
@CacheEvict 調(diào)用方法時會從緩存中移除相應(yīng)的數(shù)據(jù)
@Caching 組合多個注解策略在一個方法上

@Cacheable跟狱、@CachePut、@CacheEvit 都有 value 屬性金拒,指定緩存名稱兽肤,key 屬性指定的是數(shù)據(jù)在緩存中的存儲的鍵套腹。

開啟聲明式緩存支持

在配置類上使用 @EnableCaching 即可開啟聲明式緩存支持绪抛。

@Condiguration
@EnableCaching
public class AppConfig {

}

Spring Boot 緩存支持

在 Spring Boot 中已經(jīng)自動配置了多個 CacheManager 的實現(xiàn)资铡。在不做任何額外配置的情況下默認使用 SimpleCacheManager。Spring Boot 支持以 spring.cache 為前綴的屬性來配置緩存幢码。

spring.cache.type= # 緩存類型
spring.cache.cache-names= # 程序啟動時創(chuàng)建緩存名稱
spring.cache.ehcache.config= # ehcache配置文件地址
spring.cache.hazelcast.config= # hazelcast配置文件地址
spring.cache.jcache.provider= # 當多個 jcache 實現(xiàn)在類路徑的時候笤休,指定 jcache 實現(xiàn)
spring.cache.guava.spec= # guava specs

在 Spring Boot 環(huán)境下,只需要在項目中導入相關(guān)緩存技術(shù)的依賴包症副,并在配置類上使用 @EnableConfig 開啟緩存支持即可店雅。

新建 Spring Boot 項目,添加依賴至 pom.xml 贞铣。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

在配置文件 application.properties 中添加數(shù)據(jù)庫配置信息闹啦。

# 數(shù)據(jù)庫相關(guān)
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springstudy
spring.datasource.username=spring
spring.datasource.password=spring
spring.datasource.dirverClassName=com.mysql.jdbc.Driver

創(chuàng)建和前面相同的實體類。

package com.wyk.cachedemo.bean;

import javax.persistence.*;

@Entity
@Table(name = "t_person")
public class Person {
    @Id //映射為數(shù)據(jù)庫主鍵
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 生成方式為自增
    private Long id;
    private String name;
    private Integer age;
    private String address;

    public Person() {}

    public Person(Long id, String name, Integer age, String address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

添加實體類的 Repository 辕坝。

package com.wyk.cachedemo.repository;

import com.wyk.cachedemo.bean.Person;
import org.springframework.data.jpa.repository.JpaRepository;

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

添加業(yè)務(wù)服務(wù)接口:

package com.wyk.cachedemo.service;

import com.wyk.cachedemo.bean.Person;

public interface DemoService {
    public Person save(Person person);

    public void remove(Long id);

    public Person findOne(Person person);
}

添加業(yè)務(wù)服務(wù)實現(xiàn):

package com.wyk.cachedemo.service.impl;

import com.wyk.cachedemo.repository.PersonRepository;
import com.wyk.cachedemo.bean.Person;
import com.wyk.cachedemo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class DemoServiceImpl implements DemoService {

    @Autowired
    PersonRepository personRepository;

    @Override
    @CachePut(value="people", key="#person.id")
    public Person save(Person person) {
        Person p = personRepository.save(person);
        System.out.println("為 id窍奋、key為"  + p.getId() + "數(shù)據(jù)做了緩存");
        return p;
    }

    @Override
    @CacheEvict(value="people")
    public void remove(Long id) {
        System.out.println("刪除了id、key為" + id + "的數(shù)據(jù)庫緩存");
        personRepository.deleteById(id);
    }

    @Override
    @Cacheable(value="people", key="#person.id")
    public Person findOne(Person person) {
        Optional<Person> p = personRepository.findById(person.getId());
        System.out.println("為 id酱畅、key為"  + person.getId() + "數(shù)據(jù)做了緩存");
        if(p.isPresent()) {
            return p.get();
        } else {
            return null;
        }

    }
}

添加控制器:

package com.wyk.cachedemo.controller;

import com.wyk.cachedemo.bean.Person;
import com.wyk.cachedemo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CacheController {
    @Autowired
    DemoService demoService;

    @RequestMapping("/put")
    public Person put(Person person) {
        return demoService.save(person);
    }

    @RequestMapping("/able")
    public Person cacheable(Person person) {
        return demoService.findOne(person);
    }

    @RequestMapping("/evit")
    public String evit(Long id) {
        demoService.remove(id);
        return "ok";
    }
}

開啟緩存支持琳袄。

package com.wyk.cachedemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class CacheDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(CacheDemoApplication.class, args);
    }

}

運行程序。首先訪問 http://localhost:8080/able?id=1 纺酸。

第一次訪問會在控制臺打印“為 id窖逗、key為1數(shù)據(jù)做了緩存”,后面再次訪問則不會餐蔬,說明已經(jīng)存在于緩存中碎紊。

訪問 http://localhost:8080/put?name=tt&age=26&address=Hebei

在訪問 http://localhost:8080/able?id=11 樊诺,控制臺無輸出仗考,會直接獲得數(shù)據(jù)。

訪問 http://localhost:8080/evit?id=11 啄骇,會刪除數(shù)據(jù)及其緩存痴鳄。

切換緩存技術(shù)

切換緩存只需要在 pom.xml 中添加相應(yīng)的依賴即可,如果需要配置文件缸夹,則在類路徑下進行配置痪寻, Spring 會自動掃描。

如果我們需要使用 Guava 作為緩存技術(shù)虽惭,只需要在 pom.xml 中增加 Guava 依賴橡类。

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>18.0</version>
</dependency>

非關(guān)系型數(shù)據(jù)庫 NoSQL

NoSQL 是對不使用關(guān)系作為數(shù)據(jù)管理的數(shù)據(jù)庫系統(tǒng)的統(tǒng)稱,NoSQL 的主要特點是不使用 SQL 作為查詢語言芽唇,數(shù)據(jù)存儲也不是固定的表顾画、字段取劫。

NoSQL 數(shù)據(jù)庫主要有文檔存儲型(MongoDB)、圖形關(guān)系存儲型(Neo4j)和鍵值對存儲型(Redis)研侣。

MongoDB

Spring 支持

Spring 對 MongoDB 的支持主要通過 Spring Data MongoDB來實現(xiàn)谱邪,Spring Data MongoDB提供了如下功能。

Object/Document 映射注解支持

Spring Data MongoDB 提供如下注解庶诡。

注解 作用
@Document 映射領(lǐng)域?qū)ο笈cMongoDB的一個文檔
@Id 映射當前屬性為ID
@DbRef 當前屬性將參考其它文檔
@Filed 為文檔的屬性定義名稱
@Version 將當前屬性作為版本
@Indexed 用于字段惦银,表示該字段需要如何創(chuàng)建索引
@CompoundIndex 用于類,以聲明復合索引
@GeoSpatialIndexed 用于字段末誓,進行地理位置索引
@TextIndexed 用于字段扯俱,標記該字段要包含在文本索引中
@Language 用于字段,以設(shè)置文本索引的語言覆蓋屬性
@Transient 默認情況下喇澡,所有私有字段都映射到文檔迅栅,此注解將會去除此字段的映射
@PersistenceConstructor 標記一個給定的構(gòu)造函數(shù),即使是一個protected修飾的晴玖,在從數(shù)據(jù)庫實例化對象時使用读存。構(gòu)造函數(shù)參數(shù)通過名稱映射到檢索的DBObject中的鍵值
MongoTemplate

MongoTemplate 提供了數(shù)據(jù)訪問的方法,我們還需要為 MongoClient 以及 MongoDbFactory來配置數(shù)據(jù)庫連接屬性窜醉。

@Bean 
public MongoClient client() throws UnknowHostException {
    MongoClient client = new MongoClient(new ServerAddress("127.0.0.1",27017));
    return client;
}

@Bean
public MongoDbFactory mongoDbFactory() throws Exception {
    String database = new MongoCientURI("mongodb://localhost/test").getDataBase();
    return new SimpleMongoDbFactory(client(), database);
}

@Bean
public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory) throws UnkownHostException {
    return new MongoTemplate(mongoDbFactory);
}
Repository 支持

Spring Data MongoDB 還提供了 Repostiory 的支持宪萄,使用方式和 Spring Data JPA 一致。

public interface PersonRepository extends MongoRepository<Person, String> {

}

MongoDB 的 Repository 的支持開啟需在配置類上注解 @EnableMongoRepositories榨惰。

@Configuration
@EnableMongoRepositories
public class AppConfig {

}

Spring Boot 支持

在 Spring Boot 下使用 MongoDB拜英, 只需要引入 spring-boot-starter-data-mongodb 依賴即可,無需任何配置琅催。

其中 MongoDB 相關(guān)的信息可以在 application.properties 中以 spring.data.mongodb 為前綴進行配置居凶。

創(chuàng)建項目,添加 Web 和 MongoDB 依賴藤抡,添加如下數(shù)據(jù)庫配置信息侠碧。

spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=spring
spring.data.mongodb.username=wyk
spring.data.mongodb.password=123456

添加 Person 實體類,與前面不同的是添加了一個 location 字段缠黍。

package com.wyk.mongotest.bean;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

import java.util.Collection;
import java.util.LinkedHashSet;

@Document
public class Person {
    @Id
    private String id;
    private String name;
    private Integer age;
    @Field("locs")
    private Collection<Location> location = new LinkedHashSet<Location>();

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Collection<Location> getLocation() {
        return location;
    }

    public void setLocation(Collection<Location> location) {
        this.location = location;
    }
}

其中 Location 類的定義如下弄兜。

package com.wyk.mongotest.bean;

public class Location {
    private String place;
    private String year;

    public Location(String place, String year) {
        this.place = place;
        this.year = year;
    }

    public String getPlace() {
        return place;
    }

    public void setPlace(String place) {
        this.place = place;
    }

    public String getYear() {
        return year;
    }

    public void setYear(String year) {
        this.year = year;
    }
}

在 repository 中添加數(shù)據(jù)訪問方法。

package com.wyk.mongotest.repository;

import com.wyk.mongotest.bean.Person;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;

import java.util.List;

public interface PersonRepository extends MongoRepository<Person, String> {
    Person findByName(String name);

    @Query("{'age':?0}")
    List<Person> withQueryFindByAge(Integer age);
}

添加控制器瓷式。

package com.wyk.mongotest.contronller;

import com.wyk.mongotest.bean.Location;
import com.wyk.mongotest.bean.Person;
import com.wyk.mongotest.repository.PersonRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;

@RestController
public class DataController {
    @Autowired
    PersonRepository personRepository;

    @RequestMapping("/save")
    public Person save() {
        Person p = new Person("wyk", 30);
        Collection<Location> locations = new LinkedHashSet<>();
        Location loc1 = new Location("河北", "2006");
        Location loc2 = new Location("北京","2009");
        locations.add(loc1);
        locations.add(loc2);
        p.setLocation(locations);
        return personRepository.save(p);
    }

    @RequestMapping("/q1")
    public Person q1(String name) {
        return personRepository.findByName(name);
    }

    @RequestMapping("/q2")
    public List<Person> q2(Integer age) {
        return personRepository.withQueryFindByAge(age);
    }
}

運行程序替饿,訪問 http://localhost:8080/save 保存數(shù)據(jù)。

springboot-jpa-save.png

還可以訪問 http://localhost:8080/q1?name=wykhttp://localhost:8080/q2?age=30 查看查詢結(jié)果贸典,這里不再演示视卢。

Redis

待補充。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末廊驼,一起剝皮案震驚了整個濱河市据过,隨后出現(xiàn)的幾起案子惋砂,更是在濱河造成了極大的恐慌,老刑警劉巖绳锅,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件西饵,死亡現(xiàn)場離奇詭異,居然都是意外死亡榨呆,警方通過查閱死者的電腦和手機罗标,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門庸队,熙熙樓的掌柜王于貴愁眉苦臉地迎上來积蜻,“玉大人,你說我怎么就攤上這事彻消「筒穑” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵宾尚,是天一觀的道長丙笋。 經(jīng)常有香客問我,道長煌贴,這世上最難降的妖魔是什么御板? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮牛郑,結(jié)果婚禮上怠肋,老公的妹妹穿的比我還像新娘。我一直安慰自己淹朋,他們只是感情好笙各,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著础芍,像睡著了一般杈抢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仑性,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天惶楼,我揣著相機與錄音,去河邊找鬼诊杆。 笑死歼捐,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的刽辙。 我是一名探鬼主播窥岩,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼宰缤!你這毒婦竟也來了颂翼?” 一聲冷哼從身側(cè)響起晃洒,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎朦乏,沒想到半個月后球及,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡呻疹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年吃引,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刽锤。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡镊尺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出并思,到底是詐尸還是另有隱情庐氮,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布宋彼,位于F島的核電站弄砍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏输涕。R本人自食惡果不足惜音婶,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望莱坎。 院中可真熱鬧衣式,春花似錦、人聲如沸型奥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厢汹。三九已至螟深,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烫葬,已是汗流浹背界弧。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留搭综,地道東北人垢箕。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像兑巾,于是被迫代替她去往敵國和親条获。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353