第8章 Spring Boot的数据访问

来源:互联网 发布:js正则表达式校验邮箱 编辑:程序博客网 时间:2024/06/16 11:31

第8章 Spring Boot的数据访问
Spring Data项目是Spring用来解决数据访问问题的一揽子解决方案,Spring Data是一个伞形项目,包含了大量关系型数据库及非关系型数据库的数据访问解决方案。Spring Data使我们可以快速且简单地使用普通的数据访问技术及新的数据访问技术。
Spring Data为我们使用统一的API来对上述的数据存储技术进行数据访问操作提供了支持。这是Spring通过提供Spring Data Commons项目来实现的,它是上述各种Spring Data项目的依赖。

8.1 引入Docker
Docker是一个轻量级容器技术,类似于虚拟机技术。Docker是直接运行在当前操作系统之上,而不是运行在虚拟机中,但是也实现了虚拟机技术的资源隔离,性能远远高于虚拟机技术。
Docker支持将软件编译成一个镜像,在这个镜像里做好对软件的各种配置,然后发布这个镜像,使用者可以运行这个镜像,运行中的镜像称之为容器(container),容器的启动是非常快的,一般都是以秒为单位。
Docker大有统一云计算的趋势。
这里的云计算平台一般指的是PaaS(平台即服务),它是一个这样的云计算:平台提供了存储、数据库、网络、负载均衡、自动扩展等功能,你只需将你的程序交给云计算平台就可以了。你的程序可以是不同的编程语言开发的,而使用的Docker的云计算平台就是用Docker来实现以上功能及不同程序之间的隔离的。
目前主流的软件以及非主流的软件大部分都有人将其封装为Docker镜像,我们只需下载Docker镜像,然后运行镜像就可以快速获得已做好配置可运行的软件。
Docker并不是为开发测试方便而提供的小工具,而是可以用于实际生产环境的一种极好的部署方式。

8.1.2 Docker常用命令及参数

1.Docker镜像命令
(1)Docker镜像检索
docker search 镜像名
(2)镜像下载
docker pull 镜像名
(3)镜像列表
查看本地镜像列表
docker images
(4)镜像删除
删除指定镜像:
docker rmi image-id
删除所有镜像:
docker rmi $(docker images -q)

2.Docker容器命令
(1)容器基本操作
运行镜像为容器的命令:
docker run –name container-name -d image-name
–name参数是为容器取得名称
-d表示detached,意味着执行完这句命令后控制台将不会被阻塞,可继续输入命令操作
image-name是要使用哪个镜像来运行容器。
(2)查看运行中的容器列表:
docker ps
查看运行和停止状态的容器:
docker ps -a
(3)停止和启动容器
1)停止容器
docker stop container-name/container-id
2)启动容器
docker start container-name/container-id
3)端口映射
Docker容器中运行的软件所使用的端口,在本机和本机的局域网是不能访问的,所以我们需要将Docker容器中的端口映射到当前主机的端口上,这样我们在本机和本机所在的局域网就能够访问该软件了。
Docker的端口映射是通过一个-p参数来实现的。
docker run -d -p 6378:6379 –name port-redis redis
4)删除容器
删除单个容器:
docker rm container-id
删除所有容器:
docker rm ${docker ps -a -q}
5)容器日志
查看当前容器日志:
docker logs container-name/container-id
6)登录容器
运行中的容器其实是一个功能完备的Linux操作系统,所以我们可以像常规的系统一样登录并访问容器。

8.2 Spring Data JPA

8.2.1 点睛Spring Data JPA
JPA是一个基于O/R映射的标准规范。所谓规范即只定义标准规范(如注解、接口),不提供实现,软件提供商可以按照标准规范来实现,不提供实现,软件提供商可以按照标准规范来实现,而使用者只需按照规范中定义的方式来使用,而不用和软件提供商的实现打交道。
Spring DATA JPA是Spring Data的一个子项目,它通过提供基于JpA的Repository极大地减少了JPA作为数据访问方案的代码量。

2.定义数据访问层
使用Spring Data JPA建立数据访问层十分简单,只需定义一个继承JpaRepository的接口即可:

pulbic interface PersonRepository extends JpaRepository<Person,Long> {    //定义数据访问操作的方法,继承JpaRepository接口意味着我们默认已经有了很多数据访问操作方法。}

3.配置使用Spring Data JPA
在Spring环境中,使用Spring Data JPA可通过@EnableJpaRepositories注解来开启Spring Data JPA的支持,@EnableJpaRepositories接收的value参数用来扫描数据访问层所在包下的数据访问的接口定义。

@Configuration@EnableJpaRepositories("com.wisely.repos")public class JpaConfiguration {    @Bean     public EntityManagerFactory entityManagerFactory(){        //...    }    //还需配置DataSource、PlatformTransactionManager等相关必须bean}
  1. 定义查询方法

(1)根据属性名查询
Spring Data JPA支持通过定义在Repository接口中的方法名来定义查询,而方法名是根据实体类的属性名来确定的。
1)常规查询。根据属性名来定义查询方法:

public interface PersonRepository extends JpaRepository<Person,Long> {    /**    *通过名字相等查询,参数为name    *相当于JPQL:select p from Person p where p.name=?1    */    List<Person> findByName(String name);}

这里写图片描述
这里写图片描述
2)限制结果数量,结果数量是用top和first关键字来实现的。

public interface PersonRepository extends JpaRepository<Person,Long> {    List<Person> findFirst10ByName(String name);    List<Person> findTop30ByName(String name);}

(2) 使用JPA的NamedQuery查询
Spring Data JPA支持用JPA的NameQuery来定义查询方法,即一个名称映射一个查询语句。

@Entity@NamedQuery(name="Person.findByName",query="select p from Person p where p.name=?1")public class Person {}
public interface PersonRepository extends JpaRepository<Person,Long> {    //这时我们使用的是NamedQuery里定义的查询语句,而不是根据方法名称查询    List<Person> findByName(String name);}

(3)使用@Query查询
1)使用参数索引。Spring Data JPA还支持用@Query注解在接口的方法上实现查询

public interface PersonRepository extends JpaRopository<Person,Long> {    @Query("select p from Person p where p.address=?1")    List<Person> findByAddress(String address);}

2)
使用命名参数。上面的例子是使用参数的索引号来查询的,在Spring Data JPA里还支持在语句里用名称来匹配查询参数:

public interface PersonRepository extends JpaRepository<Person,Long> {    @Query("select p from Person p where p.address = :address")    List<Person> findByAddress(@Param("address") String address);}

3)更新查询。Spring Data JPA支持@Modifying和@Query注解组合来事件更新查询:

public interface PersonRepository extends JpaRepository<Person,Long> {    @Modifying    @Transactional    @Query(“update Person p set p.name=?1”)    Int setName(String name);}

(4)Specification
Spring Data JPA提供了一个Specification(规范)接口让我们可以更方便地构造准则查询,Specification接口定义了一个toProdicat方法用来构造查询条件。
1) 定义。接口类实现JpaSpecificationExecutor接口
public interface PersonRepository extends JpaRepository

@NoRepositoryBean//1@NoRepositoryBean指明当前这个接口不是我们领域类的接口(如PersonRepository)。public interface CustomRepository<T,ID extends Serializable> extends PagingAndSortingRepository<T,ID> {//2实现PagingAndSortingRepository接口,具备分页和排序的能力    public void doSomething(ID id);//3要定义的数据操作方法在接口中的定义。}

(2)定义接口实现:

public class CustomRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID> implements CustomRepository<T,ID> {     //1实现CustomRepository接口,继承SimpleJpaRepository类让我们可以使用其提供的方法    private final EntityManager entityManager;//2让数据操作方法中可以使用entityManager    public CustomRepositoryImpl(Class<T> domainClass,EntityManager entityManager) {        //3CustomRepositoryImpl的构造函数,需当前处理的领域类型和entityManager作为构造参数,在这里也给我们的entityManager赋值了。        super(domainClass,entityManager);        this.entityManager = entityManager;    }    @Override    public void doSomething(ID id) {        //4在此处定义数据访问操作,如调用findAll方法并构造一些查询条件。    }}

(3)自定义RepositoryFactoryBean。自定义JpaRepositoryFactoryBean替代默认RepositoryFacotryBean,我们会获得一个RepositoryFactory,RepositoryFacotory将会注册我们自定义的Repository的实现。

public class CustomRepositoryFactoryBean<T extends JpaRepository<S,ID>,S,ID extends Serializable> extends JpaRepositoryFactoryBean<T,S,ID> {//1自定义RepositoryFactoryBean,继承JpaRepositoryFactoryBean。    @Override        protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager)  {//2重写createRepositoryFactory方法,用当前的CustomRepositoryFactory创建实例    return new CustomRepositoryFactory(entityManager);    }    private static class CustomRepositoyFactory extends JpaRepositoryFactory {//3创建CustomRepositoryFactory,并继承JpaRepositoryFactory。        public CustomRepositoryFacotry(EntityManager entityManager) {            super(entityManager);        }        @Override        @SuppressWarnings({"unchecked"})        protected <T,ID extends Serializable> SimpleJpaRepository<?,?> getTargetRepository(RepositoryInformation information,EntityManager entityManager){//4重写getTargetRepository方法,获得当前自定义的Repository实现。            return new CustomRepositoryImpl<T,ID>((Class<T>)information.getDomainType(),entityManager);        }        @Override        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {//5重写getRepositoryBaseClass,获得当前自定义的Repository实现的类型。            return CustomRepositoryImpl.class;        }    }}

(4)开启自定义支持使用@EnableJpaRepositories的repositoryFactoryBeanClass来指定FactoryBea即可。
@EnableJpaRepositories(repositoryFactoryBeanClass=CustomRepositoryFactoryBean.class)

8.2.2 Spring Boot的支持
1.JDBC的自动配置
通过“spring.datasource”为前缀的属性自动配置dataSource;
Spring Boot自动开启了注解事务的支持(@EnableTransactionManagement);还配置了一个jdbcTemplate。
Spring Boot提供了一个初始化数据的功能:放置的类路径下的schema.slq文件会自动用来初始化表结构;放置在类路径下的data.sql文件会自动用来填充表数据。

2.对JPA的自动配置
Spring Boot默认JPA的实现者是Hibernate。
配置JPA可以使用spring.jpa为前缀的属性在application.properties中配置。
Spring Boot为我们配置了transactionManager、jpaVendorAdapter、entityManagerFactory等Bean。
JpaBaseConfiguration还有一个getPackagesToScan方法,可以自动扫描注解有@Entity的实体类。

3.对Spring Data JPA的自动配置
Spring Boot自动开启了对Spring Data JPA的支持,即我们无须在配置类显式声明@EnableJpaRepositories。

4.Spring Boot下的Spring Data JPA
在Spring Boot下使用Spring Data JPA,在项目的Maven依赖里添加Spring-boot-stater-data-jpa,然后只需定义DataSource、实体类和数据访问层,并在需要使用数据访问的地方注入数据访问层的Bean即可,无须任何额外的配置。

8.2.3 实战
在application.properties配置数据源和jpa

#配置数据源spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc\:mysql\://localhost\:3306/xespring.datasource.username=rootspring.datasource.password=password#配置jpa#hibernate提供了根据实体类自动维护数据库表结构的功能,可通过spring.jpa.hibernate.ddl-auto来配置。spring.jpa.hibernate.ddl-auto=update#设置hibernate操作的时候在控制台显示其真是的sql语句spring.jpa.show-sql=true#让控制台输出的json字符串格式更美观spring.jackson.serialization.indent-output=true

定义实体类

package com.wisely.domain;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;import javax.persistence.NamedQuery;@Entity //1@NamedQuery(name = "Person.withNameAndAddressNamedQuery",query = "select p from Person p where p.name=?1 and address=?2")public class Person {    @Id //2    @GeneratedValue //3    private Long id;    private String name;    private Integer age;    private String address;    public Person() {        super();    }    public Person(Long id, String name, Integer age, String address) {        super();        this.id = id;        this.name = name;        this.age = age;        this.address = address;    }    //。。。}

定义数据访问接口

package com.wisely.dao;import java.util.List;import org.springframework.data.jpa.repository.Query;import org.springframework.data.repository.query.Param;import com.wisely.domain.Person;import com.wisely.support.CustomRepository;public interface PersonRepository extends CustomRepository<Person, Long> {    //使用方法名查询,接受一个name参数,返回值为列表    List<Person> findByAddress(String address);    //使用方法名查询,接受name和address,返回值为单个对象    Person findByNameAndAddress(String name,String address);    //使用@Query查询,参数按照名称绑定    @Query("select p from Person p where p.name= :name and p.address= :address")    Person withNameAndAddressQuery(@Param("name")String name,@Param("address")String address);    //使用@NamedQuery查询,在实体类中做的@NamedQuery的定义    Person withNameAndAddressNamedQuery(String name,String address);}
package com.wisely.web;import java.util.List;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.data.domain.Sort.Direction;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import com.wisely.dao.PersonRepository;import com.wisely.domain.Person;@RestControllerpublic class DataController {    //1 Spring Data JPA已自动为你注册bean,所以可自动注入    @Autowired    PersonRepository personRepository;    /**     * 保存     * save支持批量保存:<S extends T> Iterable<S> save(Iterable<S> entities);     *      * 删除:     * 删除支持使用id,对象以,批量删除及删除全部:     * void delete(ID id);     * void delete(T entity);     * void delete(Iterable<? extends T> entities);     * void deleteAll();     *      */    @RequestMapping("/save")    public Person save(String name,String address,Integer age){        Person p = personRepository.save(new Person(null, name, age, address));        return p;    }    /**     * 测试findByAddress     */    @RequestMapping("/q1")    public List<Person> q1(String address){        List<Person> people = personRepository.findByAddress(address);        return people;    }    /**     * 测试findByNameAndAddress     */    @RequestMapping("/q2")    public Person q2(String name,String address){        Person people = personRepository.findByNameAndAddress(name, address);        return people;    }    /**     * 测试withNameAndAddressQuery     */    @RequestMapping("/q3")    public Person q3(String name,String address){        Person p = personRepository.withNameAndAddressQuery(name, address);        return p;    }    /**     * 测试withNameAndAddressNamedQuery     */    @RequestMapping("/q4")    public Person q4(String name,String address){        Person p = personRepository.withNameAndAddressNamedQuery(name, address);        return p;    }    /**     * 测试排序     */    @RequestMapping("/sort")    public List<Person> sort(){        List<Person> people = personRepository.findAll(new Sort(Direction.ASC,"age"));        return people;    }    /**     * 测试分页     */    @RequestMapping("/page")    public Page<Person> page(){        Page<Person> pagePeople = personRepository.findAll(new PageRequest(1, 2));        return pagePeople;    }    @RequestMapping("/auto")    public Page<Person> auto(Person person){        Page<Person> pagePeople = personRepository.findByAuto(person, new PageRequest(0, 10));        return pagePeople;    }}
0 0
原创粉丝点击