美文网首页SpringHomeSpring Boot
Spring Boot & Spring Data &a

Spring Boot & Spring Data &a

作者: 大批 | 来源:发表于2018-08-05 12:40 被阅读163次

    :) 本篇会分别介绍 Spring Data ,JPA ,Spring Data JPA

    JPA简介

    jpa全称是 Java Persistence API,jpa定义了各种注解(用来定义实体,映射关系)。JPA仅仅是一个规范,它的实现比较出名的是Hibernate

    JPA实体

    • 实体可以简单理解成一组状态的集合
    • 实体需要能够持久化并有持久化标识,支持事务
    • JPA中的实体使用注解@Entity标记,实体的id使用@Id标记,实体对应的数据库表名使用@Table标记,实体对应数据库字段使用@Column标记,主键生成策略使用@GeneratedValue标记
    import javax.persistence.*;
    import java.io.Serializable;
    
    @Entity
    @Table(name = "user")
    public class User  implements Serializable {
    
        @Id
        // 主键自动增长
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(name = "name")
        private String name;
    
        // 默认对应字段 number
        private String number;
    
       // 省略 getter & setter 
    }
    

    实体还需要一个默认的无参构造函数

    使用spring boot简单测试jpa

    • 添加依赖
          <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </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>
    
    • 在classpath:META-INF下面创建一个java persistence 的配置文件(注意文件路径必须是 META-INF/persistence.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.0"
                 xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
        <persistence-unit name="JPA" transaction-type="RESOURCE_LOCAL">
            <!-- 配置jpa ORM产品 -->
            <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
            <!-- 添加对应的持久化类 -->
            <class>com.suse.yudapi.entity.User</class>
            <properties>
                <!-- jpa中连接数据库 -->
                <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
                <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/demo" />
                <property name="javax.persistence.jdbc.user" value="root" />
                <property name="javax.persistence.jdbc.password" value="123456"></property>
    
                <!-- jpa中配置hibernate基本属性 -->
                <property name="hibernate.show_sql" value="true" />
                <property name="hibernate.format_sql" value="true" />
                <property name="hibernate.hbm2ddl.auto" value="create"/>
            </properties>
    
        </persistence-unit>
    </persistence>
    
    • 创建一个测试程序
    public class JPAMain {
    
        public static void main(String[] args){
            EntityManagerFactory factory = Persistence.createEntityManagerFactory("JPA");
            EntityManager manager = factory.createEntityManager();
            manager.getTransaction().begin();
            User user = new User();
            user.setName("yudapi");
            user.setNumber("12312312");
            manager.persist(user);
            manager.getTransaction().commit();
        }
    
    }
    

    JPA的CRUD操作

    • CRUD都是使用的EntityManager的方法来完成的,persist,find,remove
    public class JPAMain {
    
        public static void main(String[] args){
    
            EntityManagerFactory factory = Persistence.createEntityManagerFactory("JPA");
            EntityManager manager = factory.createEntityManager();
    
            User user = new User();
            user.setName("yudapi");
            user.setNumber("12312312");
            // 新增
            manager.getTransaction().begin();
            manager.persist(user);
            manager.getTransaction().commit();
            // 查找
            User userInfo = manager.find(User.class, 1L);
            System.out.println(userInfo.getName()+"  "+userInfo.getNumber());
            // 更新
            userInfo.setName("update after");
            manager.getTransaction().begin();
            manager.persist(userInfo);
            manager.getTransaction().commit();
            System.out.println(userInfo.getName()+"  "+userInfo.getNumber());
            // 删除
            manager.getTransaction().begin();
            manager.remove(userInfo);
            manager.getTransaction().commit();
        }
    }
    

    JPA中的集合映射

    • 类似一对多的关系,但是没有双向关联关系,也不能级联操作。
    • 新建一个Book实体,一个用户持有多个book实体,注意这个实体有一个Embeddable注解。这个注解表示这个实体可以嵌入其他实体
    @Entity
    @Table(name = "books")
    @Embeddable
    public class Book implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
        private String number;
    
        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 String getNumber() {
            return number;
        }
    
        public void setNumber(String number) {
            this.number = number;
        }
    }
    
    • 修改User实体,嵌入一个Book实体的列表
    @ElementCollection(targetClass = Book.class)
    List<Book> books;
    

    自动创建的表结构是两个实体分别创建一个表,使用了一个中间表来存储关联关系

    JPA实体间的映射

    • 一对一映射 @OneToOne,这个是通过外键来关联的。注解在那个实体里面就会在这个实体里面生成一个关联的外键
    // in user entity
    @OneToOne
    UserEx userEx; // 这个实体的id会在user表里面有一个外键字段
    
    • 一对多映射@OneToMany,这个需要添加targetEntity属性。这个是通过中间表来关联
    • 多对一映射@ManyToOne,这个会再多的一方创建一个外键
    // in user entity
    @ManyToOne(cascade = CascadeType.ALL)
    Tag tag;
    
    • 多对多映射@ManyToMany

    JPA级联操作

    • 级联操作都是 javax.persistence.CascadeType 里面的枚举,级联可以用在上面的映射关系注解
    • PERSIST 如果父实体持久存在,则其所有相关实体也将被持久化
    • MERGE 如果父实体被合并,则其所有相关实体也将被合并
    • DETACH 如果父实体被分离,那么它的所有相关实体也将被分离
    • REFRESH 如果父实体被刷新,则其所有相关实体也将被刷新
    • REMOVE 如果父实体被移除,则其所有相关实体也将被移除
    • ALL 所有上述级联操作都可以应用于与父实体相关的实体

    JPQL JPA的查询语言

    • JPQL(Java持久性查询语言)是一种面向对象的查询语言,用于对持久实体执行数据库操作。 JPQL不使用数据库表,而是使用实体对象模型来操作SQL查询

    Spring Data 介绍

    Spring Data 提供了一个数据访问层的抽象,这个抽象定义了基本的访问数据的接口。这里的数据来源就不一定是数据库了,也可能是缓存,也可能是nosql数据库等,针对不同的底层数据存储方式都可以使用同一套代码进行访问

    Spring Data 核心概念

    • spring data将数据访问对象抽象成Repository,这个是一个标记接口,仅仅是标记这个是个数据访问对象。这个接口是一个泛型接口,T代表的是存储的实体(比如这个类是操作用户的类,那么这个T就是User),ID代表这个实体对应的唯一标识
    @Indexed
    public interface Repository<T, ID> {
    }
    
    • Repository仅仅是个标记接口,并没有定义任何方法。spring data提供了一个包含基本操作的Repository,CrudRepository(这个接口是Repository的子接口)
    @NoRepositoryBean
    public interface CrudRepository<T, ID> extends Repository<T, ID> {
        <S extends T> S save(S var1);
    
        <S extends T> Iterable<S> saveAll(Iterable<S> var1);
    
        Optional<T> findById(ID var1);
    
        boolean existsById(ID var1);
    
        Iterable<T> findAll();
    
        Iterable<T> findAllById(Iterable<ID> var1);
    
        long count();
    
        void deleteById(ID var1);
    
        void delete(T var1);
    
        void deleteAll(Iterable<? extends T> var1);
    
        void deleteAll();
    }
    
    
    • 对于不同的存储技术spring data提供了不同的接口,比如JpaRepository,MongoRepository

    spring data中的这些接口都不需要实现,具体的实现是由spring data通过动态代理针对不同的存储技术实现的,我们只需要按照约定将接口定义好就可以了

    spring data demo

    • 这里先用 spring boot 来完成一个 spring data 的demo
    • 依赖(这里用了 jpa 如果不清楚就先当成一个orm框架就行了)
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </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>
    
    • 创建测试数据库(demo数据库 和 user测试表)
    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(32) DEFAULT NULL,
      `number` varchar(32) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    
    • 定义实体类
    @Entity
    @Table(name = "user")
    public class User  implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        private String name;
        private String number;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getNumber() {
            return number;
        }
    
        public void setNumber(String number) {
            this.number = number;
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    }
    
    
    • 定义User的Repository接口
    public interface UserRepository extends CrudRepository<User,Long> { }
    
    • 虽然我们定义了Repository接口但是并没有实现我们需要使用注解@EnableJpaRepositories激活Repository的自动代理
    @SpringBootApplication
    @RestController
    @EnableJpaRepositories
    public class App {
        public static void main(String[] args){
            SpringApplication.run(App.class,args);
        }
    }
    
    • 访问数据库(记得配置数据库链接信息哦,DataSourcePerporties)
    @SpringBootApplication
    @RestController
    @EnableJpaRepositories
    public class App {
    
        @Autowired
        UserRepository userRepository;
    
        @RequestMapping("/insertUser")
        public User insertUser(){
            User user = new User();
            user.setName("dapi");
            user.setNumber("123456");
            user = userRepository.save(user);
            return user;
        }
    
        public static void main(String[] args){
            SpringApplication.run(App.class,args);
        }
    }
    
    

    @EnableJpaRepositories这个注解是激活 jpa 的方式来生成代理,有些时候我们的应用可能还用了其他的存储技术,比如mogodb这个时候就可以使用@EnableMogoRepositories,@EnableJpaRepositories可以添加一个basePackages属性可以指定只代理特定包下面的Repositor接口



    ↓ 详细介绍使用spring data的详细步骤

    定义Repository接口

    • 定义我们特定的repository接口我们有好几种选择:RepositoryCrudRepositoryPagingAndSortingRepository(支持排序和分页的方法定义,这个没有什么特别的我们也可以按照规则自己定义方法),当然也可以不继承任何的接口(前面已经所有这些接口仅仅是一个标记接口)

    • 有些时候我们会定义一个根据项目需求写的一个父Repository接口,其他所有的Repository接口都需要继承这个接口。这个父Repository接口是不需要被代理的,这个时候可以在父Repository接口使用注解 @NoRepositoryBean

    • 如果不想继承spring data的Repository标记接口,可以使用注解

    @RepositoryDefinition(domainClass = User.class,idClass = Long.class)
    public interface UserRepository  {
        <S extends User> S save(S user);
    }
    
    • 多种存储技术混合用的情况,比如JPA 和 MongoDB同时用的时候。有个尴尬的事情就是,同一个Repository究竟是操作 sql数据库呢还是操作 MongoDB呢?原则就是只要有能够区分这两种操作的代码就行,比如这个Repository继承的是JpaRepository那这个就是操作sql数据库,如果操作的实体使用了@Entity注解那就是操作sql了,如果实体使用@Docment那就是操作mongodb了,如果Repository没有使用特定的Repository,实体同时使用了@Entity和@Docment那这个时候就只能使用包名来区分了,比如jpa的Repository都在一个包里面@EnableJpaRepositories(basePackages = "com.suse.re.jpa")

    定义查询方法

    • 在Repository接口中按照特定的方式定义方法,就可以实现特定的查询(当然不止这一种方式,后面在介绍其他方式)
    • 查询方法find…By,read…By, query…By,count…By, get…By。只要按照这种方式定义方法名,就可以实现查询(还有很多其他的支持,后面结合 jpa 一起说)
    @RepositoryDefinition(domainClass = User.class,idClass = Long.class)
    public interface UserRepository  {
        <S extends User> S save(S user);
    
        List<User> findByName(String name);
    }
    
    • 分页和排序处理,只需要在方法后面添加参数就可以处理了
    @RepositoryDefinition(domainClass = User.class,idClass = Long.class)
    public interface UserRepository  {
        <S extends User> S save(S user);
         
        // 定义排序
        List<User> findByName(String name, Sort orders);
    }
    
        @RequestMapping("/filterName")
        public List<User> filterName(){
            // 使用排序
            Sort orders = new Sort(Sort.Direction.DESC,"id");
            return userRepository.findByName("dapi",orders);
        }
    
    @RepositoryDefinition(domainClass = User.class,idClass = Long.class)
    public interface UserRepository  {
        <S extends User> S save(S user);
    
        List<User> findByName(String name, Pageable pageable);
    }
    
    Pageable pageable = PageRequest.of(0,1);
    return userRepository.findByName("dapi",pageable);
    

    这两个参数不能同时使用,如果想要同时支持分页和选择,仅仅是构造Pageable的时候不同。PageRequest pageable = PageRequest.of(0,1,Sort.Direction.DESC,"id");

    创建Repository实例

    • 注解@EnableJpaRepositories
    • 使用EntityManager
    class MyRepositoryImpl<T, ID extends Serializable>
            extends SimpleJpaRepository<T, ID> {
        private final EntityManager entityManager;
    
        MyRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
            super(entityInformation, entityManager);
            this.entityManager = entityManager;
        }
        @Transactional
        public <S extends T> S save(S entity) {
        }
    }
    

    spring data web

    • 开启 spring data web 支持,@EnableSpringDataWebSupport
    • 直接将参数转化成 Pageable 和 Sort
    // http://localhost:8080/filterName?page=0&size=1&sort=id,DESC
        @RequestMapping("/filterName")
        public List<User> filterName(Pageable pageable){
            // PageRequest pageable = PageRequest.of(0,1,Sort.Direction.DESC,"id");
            return userRepository.findByName("dapi",pageable);
        }
    

    Spring Data JPA简介

    • spring data jpa 是构建在spring data 之上的一个简化 jpa 操作的库

    依赖

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </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>
    

    配置

    • 两个注解:@EnableJpaRepositories@EnableTransactionManagement
    @SpringBootApplication
    @RestController
    @EnableJpaRepositories
    @EnableTransactionManagement
    public class App {
    
        @Autowired
        UserRepository userRepository;
    
        @RequestMapping("/insertUser")
        public User insertUser(){
            User user = new User();
            user.setName("dapixxx");
            user.setNumber("123456");
            user = userRepository.save(user);
            return user;
        }
    
    
        public static void main(String[] args){
            SpringApplication.run(App.class,args);
        }
    }
    

    保存实体

    • 保存实体最后会调用 EntityManager 的 persist 方法或者是 merge方法 判断是更具Id来判断了,如果Id为空会调用 persist 如果id不为空则调用 merge

    查询方法

    • 首先要知道有三种方式可以写查询方法:方法名,JPA NamedQueries ,@Query

    • 方法名:按照特定的方式命名函数,spring data 最后会解析这个函数名

    • NamedQueries: 这个是JPA里面注解查询

    • Query: 这个是spring data 的一个扩展,和上面那个类似

    • 按照方法名:比如 List<User> findByEmailAddressAndLastname(String emailAddress, String lastname); 这个方法名最后会被翻译成 select u from User u where u.emailAddress = ?1 and u.lastname = ?2。 具体支持那些关键字就看这里吧

    • @NamedQueries(JPQL) & @NamedNativeQuery(支持sql)

    @Entity
    @NamedQuery(name = "User.findByEmailAddress",
    query = "select u from User u where u.emailAddress = ?1")
    public class User {
    }
    
    public interface UserRepository extends JpaRepository<User, Long> {
          List<User> findByLastname(String lastname);
          User findByEmailAddress(String emailAddress);
    }
    
    
    • @Query
    public interface UserRepository extends JpaRepository<User, Long> {
            @Query("select u from User u where u.emailAddress = ?1")
            User findByEmailAddress(String emailAddress);
    }
    
    • @Query & Like
    public interface UserRepository extends JpaRepository<User, Long> {
            @Query("select u from User u where u.firstname like %?1")
            List<User> findByFirstnameEndsWith(String firstname);
    }
    
    • @Query & sql
    public interface UserRepository extends JpaRepository<User, Long> {
            @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery =
    true)
            User findByEmailAddress(String emailAddress);
    }
    
    • @Query & sql & page
    public interface UserRepository extends JpaRepository<User, Long> {
    @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
    Page<User> findByLastname(String lastname, Pageable pageable);
    }
    
    • @Query & name parameters
    public interface UserRepository extends JpaRepository<User, Long> {
            @Query("select u from User u where u.firstname = :firstname or u.lastname =:lastname")
            User findByLastnameOrFirstname(@Param("lastname") String lastname,
                @Param("firstname") String firstname);
    }
    

    投影

    • 简单的说就是实体的有些部分不想返回,我们就可以用投影来操作
    • 建立一个接口,将需要返回的字段声明成方法
    class Person {
        @Id UUID id;
        String firstname, lastname;
        Address address;
        static class Address {
            String zipCode, city, street;
        }
    }
    
    interface NamesOnly {
        String getFirstname();
        String getLastname();
    }
    
    interface PersonRepository extends Repository<Person, UUID> {
        Collection<NamesOnly> findByLastname(String lastname);
    }
    
    

     
    End

    [2]  spring boot JDBC
    [4]  spring boot Mybatis

    相关文章

      网友评论

        本文标题:Spring Boot & Spring Data &a

        本文链接:https://www.haomeiwen.com/subject/waxwmftx.html