zoukankan      html  css  js  c++  java
  • Spring-data-jpa

    Spring Data JPA。

       是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!

     <!--添加springdata-jpa依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>

    基础步骤:
    1. 在application.properties中加入

    spring.datasource.url=jdbc:mysql://localhost:3306/数据库名
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.username=用户名
    spring.datasource.password=密码

    2. 建立实体
    @Entity 表示这个类是一个实体,即有一个数据库表格和它对应
    @Table(name="XX") 用来指定一个实体和哪个表格对应, 其中的name属性是表格名字
    @Id 表示这个属性是主键
    @Column(name = "PublisherId", nullable = false) 用来指定一个属性和列的对应关系,其中name是列的名字,nullable表示这一列是否允许非空
    @GeneratedValue(strategy = GenerationType.IDENTITY) 主键自增
    @Basic 默认被添加,表示这个属性是会被进行持久化的

      快捷键alt+insert    equals and hashCode

    3. 建立dao层代码
    JPA的命名习惯:实体+Repository(BooksRepository),放入repository包。

    之前使用JDBC的时候,命名建议使用BooksDAO,放入dao包
    使用MyBatis的时候,命名建议使用BooksMapper,放入mapper包

    ⚠️:让BooksRepository接口继承CrudRepository接口
    CrudRepository接口需要带有范型<Books, Long>,其中第一个参数是实体的类型,第二个参数是主键的类型

    import org.springframework.data.repository.CrudRepository;
    //Spring Data自带的很多方法如果可以满足日常使用,是不需要自己书写方法的。但是有一些特殊查询是JPA没有提供的,就需要我们自己书写
    public interface BooksRepository extends CrudRepository<实体类, 主键类型> {
    }

    4.手动的对Spring Data JPA框架进行配置

    在config下建立文件

    package com.example.jpa.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.orm.jpa.JpaTransactionManager;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.SharedCacheMode;
    import javax.persistence.ValidationMode;
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    @EnableJpaRepositories(  //启用JPA和Spring Data的整合
            basePackages = "com.example.jpa.repository",  //整合后,repository接口所在的位置
            entityManagerFactoryRef = "entityManagerFactoryBean",  //实体管理器,下面一个方法的名字
            transactionManagerRef = "transactionManager") //Spring Data JPA应该让谁来管理事务?事务管理器,下面一个方法的名字
    @EnableTransactionManagement //让Spring框架进行事务的管理
    public class SpringDataJpaConfig {
    
        @Bean
        public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource) {
            Map<String, Object> properties = new HashMap<>();
            properties.put("javax.persistence.schema-generation.database.action", "none");
    
            HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
            adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect");
    
            LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
            factory.setPackagesToScan("实体包路径"); //扫描实体所在的路径
    
            factory.setJpaVendorAdapter(adapter);
            factory.setJpaPropertyMap(properties);
    
            factory.setDataSource(dataSource);
    
            factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
            factory.setValidationMode(ValidationMode.NONE);
    
            return factory;
        }
    
        @Bean
        public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
            JpaTransactionManager txManager = new JpaTransactionManager();
            txManager.setEntityManagerFactory(entityManagerFactory);
            return txManager;
        }
    }

    1. JPQL查询

    HQL全称是Hibernate Query Language
    JPQL全称是Java Persistence Query Language
    HQL和JPQL非常类似,同时也比较类似于SQL

    我们之所以使用JPQL主要原因是为了和SQL解耦合

    public interface BooksRepository extends PagingAndSortingRepository<Books, Long> {
    
    @Query(value = "from Books where author = :author and 1=0")
    List<Books> a(String author);
    }

    ⚠️value在这里是不可以省略的
    ⚠️JPQL/HQL语句中不能出现表格名和列名,必须使用实体名和属性名进行替代
    :author 类似于JDBC中的?和MyBatis中的#{author}

    2. 原生SQL查询

    public interface BooksRepository extends PagingAndSortingRepository<Books, Long> {
    @Query(value = "select * from books where author = :author", nativeQuery = true)
    List<Books> b(String author);
    }

    nativeQuery = true 表示这条语句是一个原生查询,即一条SQL语句。


    原生查询带来了一定程度的灵活性,可以根据不同的数据库书写一些“特色语句”(比如limit)
    但是原生查询也将SQL和JPA耦合的过于紧密,所以不到万不得已的情况下,尽量不要使用原生查询。


    3. 命名查询
    给一个查询起一个名字,然后将这个查询放在实体上。这种用法在工作中并不常见。

    @Entity
    @NamedQuery(name="Books.a", query = "select b from Books b where b.author = :author")
    public class Books implements Serializable {
    
    //命名查询需要放在实体上,名字要求是“返回值.方法名”(name="Books.a")
    public interface BooksRepository extends PagingAndSortingRepository<Books, Long> {
    List<Books> a(String author);
    }

    注意::Spring Data JPA有多种查询方式,如果几种同时使用的时候,优先级是什么?

    @Query注解最优先生效 ---> @NamedQuery ---> 按照方法名字的书写来判定规则

     分页和排序

    select * from books limit 5,2; 从第5条开始往下查询2条。注意数据从第0行开始。

    在JPA中实现分页和排序只需要实现PagingAndSortingRepository接口

    public interface BooksRepository extends PagingAndSortingRepository<Books, Long> {
    }
    @Test
    void contextLoads() {
    booksRepository.findAll(Sort.by(Sort.Direction.DESC, "title")).forEach(System.out::println);
    //Sort.by()按照升序或者降序进行排序,后面跟上要排序的列
    
    Page<Books> page = booksRepository.findAll(PageRequest.of(0, 4)); //查看第0页,每页里面存放4条结果
    //Page里面存放的就是分页后的内容
    System.out.println(page.getTotalElements()); //getTotalElements表示结果中一共有多少条元素
    System.out.println(page.getTotalPages()); //上面的n条记录,一共被分成了几页
    
    Iterator<Books> books = page.iterator(); //出获得了查询结果的一个迭代器
    while(books.hasNext())
    System.out.println(books.next());
    
    page = booksRepository.findAll(PageRequest.of(0, 4, Sort.by(Sort.Direction.DESC, "title")));
    
    }

    @Temporal:用来设置Date类型的属性,当然随着Date的过时,这个注解已经很少使用了。
    @Enumerated:用来映射枚举类型的字段
    @Lob:用来映射大对象
    @Transinet:作用是被标记的属性不参与持久化。也就是说这个属性和数据库表格里没有对应的列。
    @IdClass: 复合主键

    1. 建立一个复合主键类,其中包含复合主键的两个属性

    public class NameAgeKey implements Serializable {
    private String name;
    private Integer age;
    //无参,带参构造方法,getter/setter
    }

    2. 建立实体: 在复合主键的每一个属性上都加上@Id注解。然后在实体上添加@IdClass(value = 复合主键类.class)

    @Entity
    @Table(name ="xxx")
    @IdClass(value = NameAgeKey.class)
    public class Xxx {
    @Id
    private String name;
    @Id
    private Integer age;
    //其余属性,无参,带参构造方法,getter/setter
    }

    3. public interface XxxRepository extends CrudRepository<Xxx, NameAgeKey> {}

     解决N+1 SQL语句问题

    在User实体上加入

    @NamedEntityGraph(name = "userEntityGraph", 
    attributeNodes = {@NamedAttributeNode("logs")})
    
    public interface UserRepository extends CrudRepository<User, Integer> {
    @Override
    @EntityGraph(value = "userEntityGraph")
    Iterable<User> findAll();
    }

    下表描述了JPA支持的关键字以及包含该关键字的JPA命名查询方法:

    关键字

    示例

    SQL

    And

    findByLastnameAndFirstname

    … where x.lastname = ?1 and x.firstname = ?2

    Or

    findByLastnameOrFirstname

    … where x.lastname = ?1 or x.firstname = ?2

    Is,Equals

    findByFirstname,findByFirstnameIs,findByFirstnameEquals

    … where x.firstname = ?1

    Between

    findByStartDateBetween

    … where x.startDate between ?1 and ?2

    LessThan

    findByAgeLessThan

    … where x.age < ?1

    LessThanEqual

    findByAgeLessThanEqual

    … where x.age <= ?1

    GreaterThan

    findByAgeGreaterThan

    … where x.age > ?1

    GreaterThanEqual

    findByAgeGreaterThanEqual

    … where x.age >= ?1

    After

    findByStartDateAfter

    … where x.startDate > ?1

    Before

    findByStartDateBefore

    … where x.startDate < ?1

    IsNull

    findByAgeIsNull

    … where x.age is null

    IsNotNull,NotNull

    findByAge(Is)NotNull

    … where x.age not null

    Like

    findByFirstnameLike

    … where x.firstname like ?1

    NotLike

    findByFirstnameNotLike

    … where x.firstname not like ?1

    StartingWith

    findByFirstnameStartingWith

    … where x.firstname like ?1(parameter bound with appended %)

    EndingWith

    findByFirstnameEndingWith

    … where x.firstname like ?1(parameter bound with prepended %)

    Containing

    findByFirstnameContaining

    … where x.firstname like ?1(parameter bound wrapped in %)

    OrderBy

    findByAgeOrderByLastnameDesc

    … where x.age = ?1 order by x.lastname desc

    Not

    findByLastnameNot

    … where x.lastname <> ?1

    In

    findByAgeIn(Collection<Age> ages)

    … where x.age in ?1

    NotIn

    findByAgeNotIn(Collection<Age> ages)

    … where x.age not in ?1

    True

    findByActiveTrue()

    … where x.active = true

    False

    findByActiveFalse()

    … where x.active = false

    IgnoreCase

    findByFirstnameIgnoreCase

    … where UPPER(x.firstame) = UPPER(?1)

  • 相关阅读:
    聚簇索引与非聚簇索引(二级索引)的区别
    swoole介绍
    什么是mysql执行计划
    php-fpm浅析
    字段设计规范
    mysql排序规则utf8_genera_ci和utf8_bin的区别
    chrome 麦克风被禁用
    获取地址栏参数
    vue 打包去掉console debugger
    Vue less全局变量预处理加载
  • 原文地址:https://www.cnblogs.com/429lirui/p/13559195.html
Copyright © 2011-2022 走看看