Spring Data JPA

Spring Data JPA

什么是JPA?

相同处:

1.都跟数据库操作有关,JPA是jdbc的升华,升级版。

2.JDBC和JPA都是一组规范1接口。

3.都是由SUN公司推出的

不同处:

1.JDBC是有各个关系型数据库实现的,JPA是有ORM框架实现。

2.JDBC使用SQL语句和数据库通信,JPA用面向对象方式,通过ORM框架生成SQL,进行操作。

3.JPA在JDBC之上,JPA也要依赖JDBC才能操作数据库。

mybatis:小巧、方便、高效、简单、直接、半自动

半自动的ORM框架

小巧:mybatis就是jdbc封装

在国内流行

场景:在业务比较复杂的系统进行使用

hibernate:强大、方便、高效、复杂、绕弯子、全自动

全自动的ORM框架

强大:根据ORM映射生成sql

在国外流行

场景:在业务相对简单的系统进行使用,随着微服务流行。

Hibernate示例

pom.xml

<dependencies>
        
        <dependency>
            <groupId>junitgroupId>

            <artifactId>junitartifactId>

            <version>4.13version>

            <scope>testscope>

        dependency>

        
        <dependency>
            <groupId>org.hibernategroupId>

            <artifactId>hibernate-entitymanagerartifactId>

            <version>5.4.32.Finalversion>

        dependency>

        
        <dependency>
            <groupId>mysqlgroupId>

            <artifactId>mysql-connector-javaartifactId>

            <version>8.0.30version>

        dependency>


    dependencies>

实体类

package com.llg.pojo;

import lombok.Data;

import javax.persistence.*;

/**
 * 实体类
 *
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 10:50
 */
@Entity  // 作为hibernate 实体类
@Table(name = "cst_customer")  // 映射的表名
@Data
public class Customer {

    /**
     * @Id: 声明主键的配置
     * @GeneratedValue: 配置主键的生成策略
     *  strategy
     *      GenerationType.IDENTITY: 自增,mysql
     *          * 底层数据库必须支持自动增长(对id自增)
     *       GenerationType.SEQUENCE: 序列,oracle
     *          * 底层数据库必须支持序列
     *       GenerationType.TABLE: jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
     *       GenerationTYpe.AUTO: 由程序自动帮助我们选择主键生成策略
     * @Column: 配置属性和字段的映射
     *  name: 数据库中字段的名称
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cust_id")
    private Long custId; // 客户的主键

    @Column(name = "cust_name")
    private String custName;
    @Column(name = "cust_address")
    private String custAddress;



}

hibernate.cfg.xml


DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>

    <session-factory>

        
        <property name="connection.username">rootproperty>

        <property name="connection.password">rootproperty>

        <property name="connection.driver_class">com.mysql.cj.jdbc.Driverproperty>

        <property name="connection.url">jdbc:mysql:///springdata_jpaproperty>

        
        
        <property name="dialect">org.hibernate.dialect.MySQL8Dialectproperty>

        
        <property name="show_sql">trueproperty>

        
        <property name="format_sql">trueproperty>

        
        <property name="hbm2ddl.auto">updateproperty>

        
        <property name="connection.isolation">2property>

        
        <property name="use_identifier_rollback">trueproperty>

        
        <mapping class="com.llg.pojo.Customer">mapping>

        
        
        
        

        
        

        

        
        

        
        

        
        
        


    session-factory>

hibernate-configuration>

单元测试

package com.llg.test;

import com.llg.pojo.Customer;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 11:23
 */
public class HibernateTest {

    // Session 工厂 :数据库会话  代码持久化操作数据库的一个桥梁
    private SessionFactory sf;

    @Before
    public void init(){
        StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure("/hibernate.cfg.xml").build();
        sf = new MetadataSources(registry).buildMetadata().buildSessionFactory();
    }

    @Test
    public void testC(){
        // session进行持久化操作
        try(Session session = sf.openSession()){
            // 开启事务
            Transaction transaction = session.beginTransaction();
            Customer customer = new Customer();
            customer.setCustName("刘列广");
            session.save(customer);
            transaction.commit();
        }

    }

    @Test
    public void testR(){
        // session进行持久化操作
        try(Session session = sf.openSession()){
            // 开启事务
            Transaction transaction = session.beginTransaction();
            Customer customer = session.find(Customer.class,1L);
            System.out.println("customer = " + customer);
            transaction.commit();
        }

    }

    @Test
    public void testR_lazy(){
        // session进行持久化操作
        try(Session session = sf.openSession()){
            // 开启事务
            Transaction transaction = session.beginTransaction();
            Customer customer = session.load(Customer.class,1L);
            System.out.println("==========================");
            System.out.println("customer = " + customer);
            transaction.commit();
        }

    }

    @Test
    public void testU() {
        // session进行持久化操作
        try (Session session = sf.openSession()) {
            // 开启事务
            Transaction transaction = session.beginTransaction();
            Customer customer = new Customer();
            customer.setCustName("刘列广1");
            customer.setCustId(1L);
            session.update(customer);
            // session.saveOrUpdate(customer);
            transaction.commit();
        }
    }

        @Test
        public void testD(){
            // session进行持久化操作
            try(Session session = sf.openSession()){
                // 开启事务
                Transaction transaction = session.beginTransaction();
                Customer customer = new Customer();
                customer.setCustId(1L);
                session.remove(customer);
                // session.saveOrUpdate(customer);
                transaction.commit();
            }

    }

    @Test
    public void testR_HQL(){
        // session进行持久化操作
        try(Session session = sf.openSession()){
            // 开启事务
            Transaction transaction = session.beginTransaction();
            String hql = " FROM Customer where custId=:id";
            List<Customer> resultList = session.createQuery(hql, Customer.class).setParameter("id", 1L).getResultList();
            System.out.println("resultList = " + resultList);
            transaction.commit();
        }

    }

}

如果单独使用hibernate的api来进行持久化操作,则不能随意切换其他ORM框架

JPA示例

1.添加META-INF\persistence.xml


<persistence version="2.2"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
     http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">

    
    <persistence-unit
            name="hibernateJPA"
            transaction-type="RESOURCE_LOCAL">

        <description>
            Hypersistence Optimizer is a dynamic analyzing tool that can scan
            your JPA and Hibernate application and provide you tips about the
            changes you need to make to entity mappings, configurations, queries,
            and Persistence Context actions to speed up your data access layer.
        description>



        
        <provider>org.hibernate.jpa.HibernatePersistenceProviderprovider>

        
        <class>com.llg.pojo.Customerclass>

        
        <properties>
            
            <property
                    name="javax.persistence.jdbc.driver"
                    value="com.mysql.cj.jdbc.Driver"
            />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql:///springdata_jpa"
            />
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="root"/>
            
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
        properties>

    persistence-unit>

persistence>

jpa的单元测试

package com.llg.test;

import com.llg.pojo.Customer;
import org.junit.Before;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 13:34
 */
public class JpaTest {
    EntityManagerFactory factory;

    @Before
    public void init() {
        factory = Persistence.createEntityManagerFactory("hibernateJPA");
    }

    @Test
    public void testC(){
        EntityManager em = factory.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        Customer customer = new Customer();
        customer.setCustName("张三");
        customer.setCustAddress("长沙");
        em.persist(customer);

        tx.commit();
    }

    @Test
    public void testR(){
        EntityManager em = factory.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        Customer customer = em.find(Customer.class, 3L);
        System.out.println("=========================");
        System.out.println("customer = " + customer);

        tx.commit();
    }

    @Test
    public void testR_lazy(){
        EntityManager em = factory.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        Customer customer = em.getReference(Customer.class, 3L);
        System.out.println("=========================");
        System.out.println("customer = " + customer);

        tx.commit();
    }

    @Test
    public void testU(){
        EntityManager em = factory.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        Customer customer = new Customer();
        customer.setCustName("张三");
        // customer.setCustAddress("长沙");
        customer.setCustId(3L);
        /**
         * 如果指定主键:
         *  更新: 先查询 看是否变化
         *          有变化更新,如果没有变化就不更新
         *  如果没有指定主键:
         *      插入
          */

        em.merge(customer);

        tx.commit();
    }

    @Test
    public void testU_JPQL(){
        EntityManager em = factory.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        String jpql = "update Customer set custName=:name where custId=:id";
        em.createQuery(jpql)
                .setParameter("name","李四")
                .setParameter("id",1L)
                .executeUpdate();
        tx.commit();
    }

    @Test
    public void testU_SQL(){
        EntityManager em = factory.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        String sql = "update cst_customer set cust_name=:name where cust_id=:id";
        em.createNativeQuery(sql)
                .setParameter("name","王五")
                .setParameter("id",2L)
                .executeUpdate();
        tx.commit();
    }

    @Test
    public void testD(){
        EntityManager em = factory.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        Customer customer = em.find(Customer.class, 3L);

        em.remove(customer);

        tx.commit();
    }

}

JPA的对象4种状态

  • 临时状态:刚创建出来,没有与entityManager发生关系,没有被持久化,不处于entityManager中的对象。
  • 持久状态:与entityManager发生关系,已经被持久化,您可以把持久化状态当做实实在在的数据库记录。
  • 删除状态:执行remove方法,事务提交之前。
  • 游离状态:游离状态就是提交到数据库之后,事务commit后实体的状态,因为事务已经提交了,此时实体的属性任你如何改变,也不会同步到数据库,因为游离是没人管的孩子,不在持久化上下文中。

Spring Data JPA实例

我们来实现一个基于Spring Data JPA的示例感受一下和之前单独使用的区别:

依赖

1.最好在父maven项目中设置spring data统一版本管理依赖: 因为不同的spring data子项目发布时间版本不一样,你自己维护很麻烦, 这样不同的spring data子项目能保证是统一版本.

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.datagroupId>

                <artifactId>spring‐data‐bomartifactId>

                <version>2020.0.14version>

                <scope>importscope>

                <type>pomtype>

            dependency>

        dependencies>

    dependencyManagement>

2.在子项目中添加:

    <dependencies>
        <dependency>
            <groupId>org.springframework.datagroupId>

            <artifactId>spring-data-jpaartifactId>

        dependency>

        
        <dependency>
            <groupId>junitgroupId>

            <artifactId>junitartifactId>

            <version>4.13version>

            <scope>testscope>

        dependency>

        
        <dependency>
            <groupId>org.hibernategroupId>

            <artifactId>hibernate-entitymanagerartifactId>

            <version>5.4.32.Finalversion>

        dependency>

        
        <dependency>
            <groupId>mysqlgroupId>

            <artifactId>mysql-connector-javaartifactId>

            <version>8.0.30version>

        dependency>

        
        <dependency>
            <groupId>com.alibabagroupId>

            <artifactId>druidartifactId>

            <version>1.2.8version>

        dependency>

        <dependency>
            <groupId>org.springframeworkgroupId>

            <artifactId>spring-testartifactId>

            <version>5.3.3version>

        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>

            <artifactId>lombokartifactId>

            <version>1.18.28version>

        dependency>

    dependencies>

pojo

package com.llg.pojo;

import lombok.Data;

import javax.persistence.*;

/**
 * 实体类
 *
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 10:50
 */
@Entity  // 作为hibernate 实体类
@Table(name = "cst_customer")  // 映射的表名
@Data
public class Customer {

    /**
     * @Id: 声明主键的配置
     * @GeneratedValue: 配置主键的生成策略
     *  strategy
     *      GenerationType.IDENTITY: 自增,mysql
     *          * 底层数据库必须支持自动增长(对id自增)
     *       GenerationType.SEQUENCE: 序列,oracle
     *          * 底层数据库必须支持序列
     *       GenerationType.TABLE: jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
     *       GenerationTYpe.AUTO: 由程序自动帮助我们选择主键生成策略
     * @Column: 配置属性和字段的映射
     *  name: 数据库中字段的名称
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cust_id")
    private Long custId; // 客户的主键

    @Column(name = "cust_name")
    private String custName;
    @Column(name = "cust_address")
    private String custAddress;



}

spring.xml



    
    

    
        
            
            
                
                

                

            

        

        
        
    

     
    
        
        
        
        
    

    
    
    
        
    

    
    




SpringDataJPAConfig方式
package com.llg.config;

import com.alibaba.druid.pool.DruidDataSource;
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.sql.DataSource;


/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 17:07
 */
@Configuration
@EnableJpaRepositories("com.llg.repositories")
@EnableTransactionManagement
public class SpringDataJPAConfig {

    /**
     * 数据源
     * @return DataSource
     */
    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/springdata_jpa");
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(true);
        vendorAdapter.setShowSql(true);
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPackagesToScan("com.llg.pojo");
        factory.setDataSource(dataSource());
        return factory;
    }

    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory);
        return txManager;
    }

}

测试

package com.llg.test;

import com.llg.config.SpringDataJPAConfig;
import com.llg.pojo.Customer;
import com.llg.repositories.CustomerRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import java.util.Optional;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 16:49
 */
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class JpaTest {
    @Resource
    CustomerRepository repository;

    @Test
    public void testR(){
        Optional<Customer> customer = repository.findById(1L);
        System.out.println(customer.get());
    }

    @Test
    public void testC(){
        Customer customer = new Customer();
        customer.setCustName("lllg");
        repository.save(customer);
    }

    @Test
    public void testD(){
        Customer customer = new Customer();
        customer.setCustId(2L);
        repository.delete(customer);
    }



}

使用 Spring Data Repositories

Spring Data repository 抽象的目标是显着减少为各种持久性存储实现数据访问层所需的样板代码量。

CrudRepository

    // 用来插入和修改 有主键就修改,没有主键就是新增
    // 获得插入后自增id,获取返回值
    <S extends T> S save(S entity);
    
    // 通过集合保存多个实体
    <S extends T> Iterable<S> saveAll(Iterable<S> entities);

    // 通过主键查询实体
    Optional<T> findById(ID id);

    // 通过主键查询是否存在 返回boolean
    boolean existsById(ID id);

    // 查询所有
    Iterable<T> findAll();

    // 通过集合的主键 查询多个实体,, 返回集合
    Iterable<T> findAllById(Iterable<ID> ids);
    
    // 查询总数量
    long count();

    // // 根据id进行删除
    void deleteById(ID id);
    
    // 根据实体进行删除
    void delete(T entity);

    //  删除多个
    void deleteAllById(Iterable<? extends ID> ids);

    // 删除多个传入集合实体
    void deleteAll(Iterable<? extends T> entities);
    
    // 删除所有
    void deleteAll();

在 之上CrudRepository,有一个PagingAndSortingRepository抽象,它添加了额外的方法来简化对实体的分页访问

    @Test
    public void testPage(){
        Page page = repository.findAll(PageRequest.of(0, 2));
        System.out.println("getTotalPages = " + page.getTotalPages());
        System.out.println("getTotalPages = " + page.getTotalPages());
        System.out.println("getContent = " + page.getContent());
    }

    @Test
    public void testSort(){
        Sort sort = Sort.by("custId").descending();
        Iterable all = repository.findAll(sort);
        System.out.println("all = " + all);
    }

    @Test
    public void testSortTypeSafe(){
        Sort.TypedSort sortType = Sort.sort(Customer.class);
        Sort sort = sortType.by(Customer::getCustId).descending();
        Iterable all = repository.findAll(sort);
        System.out.println("all = " + all);
    }

自定义操作:

1.jpql(原生SQL)

@Query

查询如果返回单个实体 就用pojo接收 , 如果是多个需要通过集合

参数设置方式

  1. 索引 : ?数字
  2. 具名: :参数名 结合@Param注解指定参数名字

增删改:

  1. 要加上事务的支持:
  2. 如果是插入方法:一定只能在hibernate下才支持 (Insert into…select )
 @Transactional // 通常会放在业务逻辑层上面去声明
 @Modifying // 通知springdatajpa 是增删改的操作

测试

package com.llg.repositories;

import com.llg.pojo.Customer;
import org.hibernate.annotations.Table;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 16:48
 */
@Repository
public interface CustomerRepository extends PagingAndSortingRepository<Customer,Long> {

    // 增删查改

    // 查询
    // @Query("from Customer where custName=?1")
    // Customer findCustomerByCustName(String custName);
    // 查询
    @Query("from Customer where custName=:custName")
    List<Customer> findCustomerByCustName(@Param("custName") String custName);

    // 修改
    @Transactional // 事务
    @Modifying // 通知springdatajpa 是增删改操作
    @Query("update Customer c set c.custName=:custName where c.custId=:id")
    int updateCustomer(@Param("custName") String custName,@Param("id") Long id);

    // 删除
    @Transactional // 事务
    @Modifying // 通知springdatajpa 是增删改操作
    @Query("delete from Customer c  where c.custId=:id")
    int deleteCustomer(@Param("id") Long id);

    // 新增
    @Transactional // 事务
    @Modifying // 通知springdatajpa 是增删改操作
    @Query("insert into Customer(custName) select c.custName from Customer c where c.custId=?1")
    int insertCustomerBySelect(@Param("id") Long id);

    // 原生sql
    @Query(value = "select * from cst_customer where cust_name=:custName",nativeQuery = true)
    List<Customer> findCustomerByCustNameBySql(@Param("custName") String custName);

}

package com.llg.test;

import com.llg.config.SpringDataJPAConfig;
import com.llg.pojo.Customer;
import com.llg.repositories.CustomerRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 18:20
 */
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class JpqlTest {

    @Resource
    CustomerRepository repository;

    @Test
    public void findCustomerByCustName(){
        List<Customer> customerByCustName = repository.findCustomerByCustName("王");
        System.out.println("customerByCustName = " + customerByCustName);
    }

    @Test
    public void updateCustomer(){
        int i = repository.updateCustomer("浏览量", 4L);
        System.out.println("updateCustomer = " + i);
    }

    @Test
    public void insertCustomerBySelect(){
        int i = repository.insertCustomerBySelect( 4L);
        System.out.println("updateCustomer = " + i);
    }

    @Test
    public void findCustomerByCustNameBySql(){
        List<Customer> customerByCustName = repository.findCustomerByCustNameBySql("王五");
        System.out.println("customerByCustName = " + customerByCustName);
    }


}

2.规定方法名

支持的查询方法主题关键字(前缀)

	**决定当前方法作用**

	只支持查询和删除

如:

查询类型:findBy ,readBy,getBy, queryBy, searchBy streamBy

判断是否存在类型:existsBy

计数:countBy

删除查询:deleteBy , removeBy

限制查询结果:First top 放在find和by之间

去重返回:Distinct

支持的查询方法谓词关键字和修饰符

	**决定查询条件**

Distinct 示例:findDistinctByName

And 示例:findByNameAndAge

Or

Is,Equals

Between

LessThan <

LessThanEqual <=

GreaterThan >

GreaterThanEqual >=

After 日期之前

Before 日期之后

IsNull,Null

IsNotNull,NotNull

Like

NotLike

StartingWith 前模糊

EndingWith 后模糊

OrderBy

Not

In

NotIn

True

False

IgoreCase

测试规定方法名使用

package com.llg.repositories;

import com.llg.pojo.Customer;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 16:48
 */
@Repository
public interface CustomeMethodNamerRepository extends PagingAndSortingRepository<Customer,Long> {

    List<Customer> findByCustName(String custName);

    Boolean existsByCustName(String custName);

    @Transactional
    @Modifying
    int deleteByCustName(String custName);

    List<Customer> findByCustNameLike(String custName);

}

单元测试

package com.llg.test;

import com.llg.config.SpringDataJPAConfig;
import com.llg.pojo.Customer;
import com.llg.repositories.CustomeMethodNamerRepository;
import com.llg.repositories.CustomerRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 18:20
 */
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class MethondNameTest {

    @Resource
    CustomeMethodNamerRepository repository;

    @Test
    public void findByCustName(){
        List customerByCustName = repository.findByCustName("浏览量");
        System.out.println("customerByCustName = " + customerByCustName);
    }

    @Test
    public void existsByCustName(){
        Boolean customerByCustName = repository.existsByCustName("浏览量");
        System.out.println("customerByCustName = " + customerByCustName);
    }

    @Test
    public void deleteByCustName(){
        int deleteByCustName = repository.deleteByCustName("浏览量");
        System.out.println("deleteByCustName = " + deleteByCustName);
    }

    @Test
    public void findByCustNameLike(){
        List findByCustNameLike = repository.findByCustNameLike("%四");
        System.out.println("findByCustNameLike = " + findByCustNameLike);
    }




}

3.Query by Example

只支持查询

不支持嵌套或分组的属性约束,如 firstname = ?0 or (firstname = ?1 and lastname = ?2).

只支持字符串 start/contains/ends/regex 匹配和其他属性类型的精确匹配。

仅支持字符串的 开始、结束、包含、正则匹配 , 其他类型的精确匹配。

实现:

1.将Repository继承QueryByExampleExecutor

package com.llg.repositories;

import com.llg.pojo.Customer;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 16:48
 */
@Repository
public interface CustomerRepositoryQueryByExample extends PagingAndSortingRepository<Customer,Long>, QueryByExampleExecutor<Customer> {


}

测试用例

package com.llg.test;

import com.llg.config.SpringDataJPAConfig;
import com.llg.pojo.Customer;
import com.llg.repositories.CustomeMethodNamerRepository;
import com.llg.repositories.CustomerRepositoryQueryByExample;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 18:20
 */
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class QueryByExample {

    @Resource
    CustomerRepositoryQueryByExample repository;

    @Test
    public void findAll(){
        // 查询条件
        Customer customer = new Customer();
        customer.setCustName("李四");
        customer.setCustAddress("1");
        // 构建查询
        Example<Customer> of = Example.of(customer);
        List<Customer> customerByCustName = (List<Customer>) repository.findAll(of);
        System.out.println("customerByCustName = " + customerByCustName);
    }



    @Test
    public void findAll2(){
        // 查询条件
        Customer customer = new Customer();
        customer.setCustName("四");
        customer.setCustAddress("Jing");
        // 创建条件匹配器
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withIgnorePaths("custName") // 设置忽略的属性
                .withIgnoreCase("custAddress") //  设置互联大小写
                .withStringMatcher(ExampleMatcher.StringMatcher.ENDING) // 设置所有字符串匹配规则;
                .withMatcher("custAddress",m -> m.endsWith()) ;//针对单个条件进行限制
        // 构建查询
        Example<Customer> example = Example.of(customer,matcher);
        List<Customer> customerByCustName = (List<Customer>) repository.findAll(example);
        System.out.println("customerByCustName = " + customerByCustName);
    }


}

4.通过Specifications

在之前使用Query by Example只能针对字符串进行条件设置,那如果希望对所有类型支持,可以使用Specifications

package com.llg.repositories;

import com.llg.pojo.Customer;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import org.springframework.stereotype.Repository;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 16:48
 */
@Repository
public interface CustomerRepositorySpecifications extends PagingAndSortingRepository<Customer,Long>, JpaSpecificationExecutor<Customer>{


}

单元测试

package com.llg.test;

import com.llg.config.SpringDataJPAConfig;
import com.llg.pojo.Customer;
import com.llg.repositories.CustomerRepositoryQueryByExample;
import com.llg.repositories.CustomerRepositorySpecifications;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-23 18:20
 */
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class Specification {

    @Resource
    CustomerRepositorySpecifications repository;

    /**
     * 地址  精确
     */
    @Test
    public void findAll(){
        List<Customer> customerList = repository.findAll(new org.springframework.data.jpa.domain.Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                // root  from Customer , 获取列
                // CriteriaBuilder  where 设置各种条件(>,<,in..)
                // query  组合(order by,where)
                Path<Object> custId = root.get("custId");
                Path<Object> custName = root.get("custName");
                Path<Object> custAddress = root.get("custAddress");
                // 参数1 代表为哪个字段设置条件 参数2:值
                Predicate predicate = criteriaBuilder.equal(custAddress, "BEIJING");

                return predicate;
            }
        });
        System.out.println("customerList = " + customerList);
    }

    /**
     * 查询客户范围(in)
     * id > 大于
     * 地址  精确
     */
    @Test
    public void findAll2(){

        List<Customer> customerList = repository.findAll((root, query, criteriaBuilder) -> {
            // root  from Customer , 获取列
            // CriteriaBuilder  where 设置各种条件(>,<,in..)
            // query  组合(order by,where)
            Path<Long> custId = root.get("custId");
            Path<String> custName = root.get("custName");
            Path<String> custAddress = root.get("custAddress");
            // 参数1 代表为哪个字段设置条件  参数2:值
            Predicate custAddressP = criteriaBuilder.equal(custAddress, "BEIJING");
            Predicate custIdP = criteriaBuilder.greaterThan(custId, 2L);
            CriteriaBuilder.In<String> in = criteriaBuilder.in(custName);
            in.value("李四");
            in.value("赵六");

            Predicate predicate = criteriaBuilder.and(custAddressP, custIdP,in);
            return predicate;
        });
        System.out.println("customerList = " + customerList);
    }


    /**
     * 查询客户范围(in)
     * id > 大于
     * 地址  精确
     */
    @Test
    public void findAll3(){
        Customer params = new Customer();
        params.setCustAddress("beijing");
        params.setCustId(1L);
        params.setCustName("李四,赵六");
        List<Customer> customerList = repository.findAll((root, query, criteriaBuilder) -> {
            // root  from Customer , 获取列
            // CriteriaBuilder  where 设置各种条件(>,<,in..)
            // query  组合(order by,where)
            Path<Long> custId = root.get("custId");
            Path<String> custName = root.get("custName");
            Path<String> custAddress = root.get("custAddress");
            // 参数1 代表为哪个字段设置条件  参数2:值
            List<Predicate> list = new ArrayList<>();
            if (!StringUtils.isEmpty(params.getCustAddress())) {
                list.add(criteriaBuilder.equal(custAddress, "BEIJING"));
            }
            if (params.getCustId() > -1) {

                list.add(criteriaBuilder.greaterThan(custId, 2L));
            }
            if (!StringUtils.isEmpty(params.getCustName())) {
                CriteriaBuilder.In<String> in = criteriaBuilder.in(custName);
                in.value("李四");
                in.value("赵六");
                list.add(in);
            }


            Predicate predicate = criteriaBuilder.and(list.toArray(new Predicate[list.size()]));
            return predicate;
        });
        System.out.println("customerList = " + customerList);
    }

    /**
     * 查询客户范围(in)
     * id > 大于
     * 地址  精确
     */
    @Test
    public void findAll4(){
        Customer params = new Customer();
        params.setCustAddress("beijing");
        params.setCustId(1L);
        params.setCustName("李四,赵六");
        List<Customer> customerList = repository.findAll((root, query, criteriaBuilder) -> {
            // root  from Customer , 获取列
            // CriteriaBuilder  where 设置各种条件(>,<,in..)
            // query  组合(order by,where)
            Path<Long> custId = root.get("custId");
            Path<String> custName = root.get("custName");
            Path<String> custAddress = root.get("custAddress");
            // 参数1 代表为哪个字段设置条件  参数2:值
            List<Predicate> list = new ArrayList<>();
            if (!StringUtils.isEmpty(params.getCustAddress())) {
                list.add(criteriaBuilder.equal(custAddress, "BEIJING"));
            }
            if (params.getCustId() > -1) {

                list.add(criteriaBuilder.greaterThan(custId, 2L));
            }
            if (!StringUtils.isEmpty(params.getCustName())) {
                CriteriaBuilder.In<String> in = criteriaBuilder.in(custName);
                in.value("李四");
                in.value("赵六");
                list.add(in);
            }


            Predicate predicate = criteriaBuilder.and(list.toArray(new Predicate[list.size()]));
            Order desc = criteriaBuilder.desc(custId);
            return query.where(predicate).orderBy(desc).getRestriction();
        });
        System.out.println("customerList = " + customerList);
    }


}

5.通过Querydsl

QueryDSL是基于ORM框架或SQL平台上的一个通用查询框架。借助QueryDSL可以在任何支持的ORM框架或SQL平台

上以通用API方式构建查询。

JPA是QueryDSL的主要集成技术,是JPQL和Criteria查询的代替方法。目前QueryDSL支持的平台包括

JPA,JDO,SQL,Mongodb 等等。

Querydsl扩展能让我们以链式方式代码编写查询方法。该扩展需要一个接口QueryDslPredicateExecutor,它定义了很

多查询方法。

接口继承了该接口,就可以使用该接口提供的各种方法了

引入依赖

        
        <dependency>
            <groupId>com.querydslgroupId>

            <artifactId>querydsl-jpaartifactId>

            <version>5.0.0version>

        dependency>

        
        <dependency>
            <groupId>com.querydslgroupId>

            <artifactId>querydsl-aptartifactId>

            <version>5.0.0version>

            <scope>providedscope>

        dependency>

添加maven插件

这个插件是为了让程序自动生成query type(查询实体,命名方式为:“Q”+对应实体名)。

<build>
        <plugins>
            <plugin>
                <groupId>com.mysema.mavengroupId>

                <artifactId>apt-maven-pluginartifactId>

                <version>1.1.3version>

                <executions>
                    <execution>
                        <goals>
                            <goal>processgoal>

                        goals>

                        <configuration>
                            <outputDirectory>target/generated-sources/queriesoutputDirectory>

                            <processor>com.querydsl.apt.jpa.JPAAnnotationProcessorprocessor>

                        configuration>

                    execution>

                executions>

            plugin>

        plugins>

    build>

执行mvn compile之后,可以找到该target/generated-sources/queries,然后IDEA标示为源代码目录即可.

QuerydslPredicateExecutor查询结果:

 @Test
    public void test01(){
        QCustomer customer = QCustomer.customer;
        //通过id查找
        BooleanExpression eq = customer.custId.eq(2L);

        Optional<Customer> one = repository.findOne(eq);
        System.out.println("one = " + one);
    }

    /**
     * 查询客户名称范围
     * id 大于
     * 地址 精确
     */
    @Test
    public void test02(){
        QCustomer customer = QCustomer.customer;
        // 客户名称范围
        BooleanExpression and = customer.custName.in("李四", "王五", "赵六")
                .and(customer.custId.gt(2L))
                .and(customer.custAddress.eq("beijing"));
        Iterable<Customer> all = repository.findAll(and);
        System.out.println("all = " + all);
    }


    /**
     * 查询客户名称范围
     * id 大于
     * 地址 精确
     */
    @Test
    public void test03(){
        Customer params = new Customer();
        // params.setCustId(2L);
        params.setCustName("李四,王五,赵六");
        params.setCustAddress("beijing");
        QCustomer customer = QCustomer.customer;
        // 初始条件,类似与1=1  永远成立
        BooleanExpression expression = customer.isNotNull().or(customer.isNull());

        expression = params.getCustId() !=null ? expression.and(customer.custId.gt(params.getCustId())) : expression;

        expression = !StringUtils.isEmpty(params.getCustName())?expression.and(customer.custName.in(params.getCustName().split(","))):expression;

        expression = !StringUtils.isEmpty(params.getCustAddress())?expression.and(customer.custAddress.eq(params.getCustAddress())):expression;

        Iterable<Customer> all = repository.findAll(expression);
        System.out.println("all = " + all);
    }

自定义quaerydsl:

 @PersistenceContext // 解决线程安全问题
    EntityManager em;

    /**
     * 自定义列查询 、 分组
     * 需要使用原生态的方式(Specification)
     * 通过Repository进行查询,列、表都是固定
     */
    @Test
    public void test04(){
        JPAQueryFactory queryFactory = new JPAQueryFactory(em);

        QCustomer customer = QCustomer.customer;
        // 构建基于QUeryDSL的查询
        JPAQuery<Tuple> tupleJPAQuery = queryFactory.select(customer.custId, customer.custName)
                .from(customer)
                .where(customer.custId.eq(1L))
                .orderBy(customer.custId.desc());
        // 执行查询
        List<Tuple> fetch = tupleJPAQuery.fetch();
        // 处理返回数据
        for (Tuple tuple : fetch) {
            System.out.println(tuple.get(customer.custId));
            System.out.println(tuple.get(customer.custName));
        }

    }


    /**
     * 自定义列查询 、 分组
     * 需要使用原生态的方式(Specification)
     */
    @Test
    public void test05(){
        JPAQueryFactory queryFactory = new JPAQueryFactory(em);

        QCustomer customer = QCustomer.customer;
        // 构建基于QUeryDSL的查询
        JPAQuery<Long> longJPAQuery = queryFactory.select(customer.custId.sum())
                .from(customer);
        // .where(customer.custId.eq(1L))
                // .orderBy(customer.custId.desc());
        // 执行查询
        List<Long> fetch = longJPAQuery.fetch();
        // 处理返回数据
        for (Long sum : fetch) {
            System.out.println(sum);
        }

    }

多表关联操作

一对一

JPA的@OneToOne注解中各个属性的默认值如下:

  1. targetEntity: 默认为当前标注的实体类。
  2. cascade: 默认值为空数组,即不进行级联操作。
  3. fetch: 默认值为EAGER,表示关系类在主类加载的时候同时加载。
  4. mappedBy: 默认值为空字符串""。
  5. optional: 默认值为true。
  6. orphanRemoval: 默认值为false。
    /**
      cascade 设置关联操作
        ALL, 所有持久化操作
        PERSIST 只有插入才会执行关联操作
        MERGE, 只有修改才会执行关联操作
        REMOVE, 只有删除才会执行关联操作
     fetch 设置是否懒加载
        EAGER 立即加载(默认)
        LAZY 懒加载( 直到用到对象才会进行查询,因为不是所有的关联对象 都需要用到)
     orphanRemoval 关联移除(通常在修改的时候会用到)一旦把关联的数据设置null ,或者修改为其他的关联数据, 如果想删除关联数据, 就可以设置true
     optional 限制关联的对象不能为null true 可以为null(默认 ) false 不能为null
     mappedBy 将外键约束执行另一方维护(通常在双向关联关系中,会放弃一方的外键约束)
         值= 另一方关联属性名
     * */

以下是@JoinColumn注解的一些常用属性:

  1. name: 指定关联的数据库列的名称。如果不设置,默认会使用属性名加上"_id"作为列名。
  2. referencedColumnName: 指定被引用表(即关联表)中列的名称。如果未指定,则默认为被引用表的主键列。
  3. unique: 指定该列是否唯一。默认为false。
  4. nullable: 指定该列是否可以为null。默认为true。
  5. insertable: 指定是否允许插入操作影响该列。默认为true。
  6. updatable: 指定是否允许更新操作影响该列。默认为true。
  7. columnDefinition: 指定列的定义SQL片段,这可以用来覆盖自动生成的列定义。
  8. table: 指定包含该外键列的表的名称。
  9. foreignKey: 指定外键约束的详细信息。

实现:

1.配置关联关系

@OneToOne

@JoinColumn(name=“外键字段名”)

    @OneToOne
    @JoinColumn(name = "account_id")
    private Account account;

account

package com.llg.pojo;

import lombok.Data;

import javax.persistence.*;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-24 14:23
 */
@Data
@Entity
@Table(name = "tb_account")
public class Account {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    private String password;
}

2.配置关联操作:

    /**
      cascade 设置关联操作
        ALL, 所有持久化操作
        PERSIST 只有插入才会执行关联操作
        MERGE, 只有修改才会执行关联操作
        REMOVE, 只有删除才会执行关联操作
     fetch 设置是否懒加载
        EAGER 立即加载(默认)
        LAZY 懒加载( 直到用到对象才会进行查询,因为不是所有的关联对象 都需要用到)
     orphanRemoval 关联移除(通常在修改的时候会用到)一旦把关联的数据设置null ,或者修改为其他的关联数据, 如果想删除关联数据, 就可以设置true
     optional 限制关联的对象不能为null true 可以为null(默认 ) false 不能为null
     mappedBy 将外键约束执行另一方维护(通常在双向关联关系中,会放弃一方的外键约束)
         值= 另一方关联属性名
     * */
    @OneToOne(cascade = CascadeType.ALL,
            fetch = FetchType.LAZY,
            orphanRemoval=true,
            optional=false,
            mappedBy = "customer")
    @JoinColumn(name = "customer_id")
    private Account account;

测试:

package com.llg;

import com.llg.config.SpringDataJPAConfig;
import com.llg.pojo.Account;
import com.llg.pojo.Customer;
import com.llg.repositories.CustomerRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Optional;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-24 14:37
 */
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class OneToOneTest {

    @Resource
    private CustomerRepository repository;

    @Test
    public void testC(){
        Account account = new Account();
        account.setUsername("llg");
        account.setPassword("123");
        Customer customer = new Customer();
        customer.setCustName("刘列广");
        customer.setCustAddress("上海嘉定区");
        customer.setAccount(account);
        account.setCustomer(customer);
        repository.save(customer);
    }

    @Test
    public void testR(){
        Optional<Customer> customer = repository.findById(2L);
        System.out.println(customer.get());
    }

    @Test
    // 为什么懒加载要配置事务
    // 当通过repository调用查询方法,session就会立即关闭,一但session关闭你就不能查询,
    // 加了事务后,就能让session直到事务执行完毕后才会关闭
    @Transactional(readOnly = true)
    public void testR_lazy(){
        Optional<Customer> customer = repository.findById(2L);
        System.out.println("=======================================");
        System.out.println(customer.get());
    }

    @Test
    public void testD(){
        repository.deleteById(2L);
    }

    @Test
    public void testU(){
        Customer customer = new Customer();
        customer.setCustName("刘列广");
        customer.setCustId(3L);
        customer.setCustAddress("上海嘉定区谢春路");
        customer.setAccount(null);
        repository.save(customer);
    }

}

差异:-

这两个设置之间的区别在于对 断开关系.例如,当设置 地址字段设置为null或另一个Address对象.

如果指定了 orphanRemoval = true ,则会自动删除断开连接的Address实例.这对于清理很有用 没有一个不应该存

在的相关对象(例如地址) 来自所有者对象(例如员工)的引用.

如果仅指定 cascade = CascadeType.REMOVE ,则不会执行任何自动操作,因为断开关系不是删除操作

一对多

实现:

1.配置关联关系

@OneToMany

@JoinColumn(name=“customer_id”)

    // 一对多
    @OneToMany(cascade = CascadeType.ALL,orphanRemoval = true)
    @JoinColumn(name="customer_id")
    private List<Messsage> messsages;

2.配置关联操作

测试一对多

package com.llg;

import com.llg.config.SpringDataJPAConfig;
import com.llg.pojo.Account;
import com.llg.pojo.Customer;
import com.llg.pojo.Messsage;
import com.llg.repositories.CustomerRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-24 14:37
 */
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class OneToManyTest {

    @Resource
    private CustomerRepository repository;

    @Test
    public void testC(){

        Customer customer = new Customer();
        customer.setCustName("刘列广");
        customer.setCustAddress("上海嘉定区");
        List<Messsage> messsageList = new ArrayList<>();
        messsageList.add(new Messsage("你好"));
        messsageList.add(new Messsage("在吗"));
        customer.setMesssages(messsageList);

        repository.save(customer);
    }

    @Test
    @Transactional(readOnly = true)
    public void testR(){
        Optional<Customer> customer = repository.findById(1L);
        System.out.println("===============================");
        System.out.println("customer = " + customer);
    }



}

测试多对一

package com.llg;

import com.llg.config.SpringDataJPAConfig;
import com.llg.pojo.Customer;
import com.llg.pojo.Messsage;
import com.llg.repositories.CustomerRepository;
import com.llg.repositories.MessageRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-24 14:37
 */
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ManyToOneTest {

    @Resource
    private MessageRepository repository;

    @Test
    public void testC(){

        Customer customer = new Customer();
        customer.setCustName("刘列广");
        customer.setCustAddress("上海嘉定区");
        List<Messsage> messsageList = new ArrayList<>();
        messsageList.add(new Messsage("你好",customer));
        messsageList.add(new Messsage("在吗",customer));

        repository.saveAll(messsageList);
    }

    @Test
    public void testR(){
        Customer customer = new Customer();
        customer.setCustId(1L);
        customer.setCustName("llg");
        List<Messsage> byCustomer = repository.findByCustomer(customer);
        // 隐式调用toString方法
        System.out.println("byCustomer = " + byCustomer);
    }




}

多对多

1.配置管理关系

@ManyToMany

@JoinColumn(name=“customer_id”)

2.配置关联操作:

    // 单向多对多
    @ManyToMany(cascade = CascadeType.ALL)
    /**
     * 中间表需要通过@JoinTable维护外键
     * name: 指定中间表的名称
     * joinColumns 设置本表的外键名称
     * inverseJoinColumns  设置关联表的外键名称
     */
    @JoinTable(
            name = "tb_customer_role",
            joinColumns = {@JoinColumn(name = "c_id")},
            inverseJoinColumns = {@JoinColumn(name = "r_id")}
    )
    private List<Role> roles;

3.测试

package com.llg;

import com.llg.config.SpringDataJPAConfig;
import com.llg.pojo.Customer;
import com.llg.pojo.Messsage;
import com.llg.pojo.Role;
import com.llg.repositories.CustomerRepository;
import com.llg.repositories.MessageRepository;
import com.llg.repositories.RoleRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.Commit;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @author llg
 * @slogan 致敬大师,致敬未来的你
 * @create 2024-09-24 14:37
 */
@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ManyToManyTest {

    @Resource
    private CustomerRepository repository;

    @Resource
    private RoleRepository roleRepository;

    @Test
    public void testC(){

        Customer customer = new Customer();
        customer.setCustName("刘列广");
        customer.setCustAddress("上海嘉定区");
        List<Role> role = new ArrayList<>();
        role.add(new Role("管理员"));
        role.add(new Role("超级管理员"));
        customer.setRoles(role);
        repository.save(customer);
    }

    /**
     * 保存,
     * 1.如果希望保存的关联数据使用已有的,就需要从数据库中查出来(持久状态),否则 提示游离状态
     * 2.如果一个业务方法有多个持久化操作,记得加上@Transactional,否则不能共用一个session
     */
    @Test
    @Transactional
    @Commit
    public void testC2(){

        Customer customer = new Customer();
        customer.setCustName("彭于晏");
        customer.setCustAddress("上海嘉定区");
        List<Role> role = new ArrayList<>();
        role.add(roleRepository.findById(1L).get());
        role.add(roleRepository.findById(2L).get());
        role.add(new Role("超级管理员"));
        customer.setRoles(role);
        repository.save(customer);
    }

    @Test
    @Transactional(readOnly = true)
    public void testR(){
        System.out.println(repository.findById(1L).get());
    }



}

多对多其实不适合删除, 因为经常出现数据出现可能除了和当前这端关联还会关联另一端,此时删除就会: ConstraintViolationException。 要删除, 要保证没有额外其他另一端数据关联

乐观锁

hibernate

防止并发修改

private @Version Long version;

审计

如何使用审计功能

首先申明实体类,需要在类上加上注解@EntityListeners(AuditingEntityListener.class),其次在application启动类中加上注解

EnableJpaAuditing,同时在需要的字段上加上@CreatedDate、@CreatedBy、@LastModifiedDate、@LastModifiedBy等注解。

这个时候,在jpa.save方法被调用的时候,时间字段会自动设置并插入数据库,但是CreatedBy和LastModifiedBy并没有赋值,因为需要实现AuditorAware接口来返回你需要插入的值

回你需要插入的值。

1.编写AuditorAware

 // AuditorAware 返回当前用户
    @Bean
    public AuditorAware<String> auditorAware(){
       return new AuditorAware() {
            @Override
            public Optional getCurrentAuditor() {
                // 当前用户
                return Optional.of("刘列广");
            }
        };
    }

2.在实体类中声明@EntityListeners和相应的注解

@EntityListeners(AuditingEntityListener.class)
@CreatedBy
private String createdBy;
@LastModifiedBy
private String modifiedBy;
@CreatedDate
@Temporal(TemporalType.TIMESTAMP)
private Date dateCreated = new Date();
@LastModifiedDate
@Temporal(TemporalType.TIMESTAMP)
private Date dateModified = new Date();

3.在Application 中启用审计@EnableJpaAuditing

@EnableJpaAuditing
<dependency>
    <groupId>org.springframeworkgroupId>

    <artifactId>spring‐aspectsartifactId>

    <version>5.3.10version>

    <scope>testscope>

dependency>

经过测试如果你的实体类上面的多个字段使用了@CreatedBy这样的注解,只会有一个生效,也就是说在一次请求中,只会被调用一次

Springboot集成JPA

         
        <dependency>
            <groupId>org.springframework.bootgroupId>

            <artifactId>spring-boot-starter-data-jpaartifactId>

        dependency>

        
        <dependency>
            <groupId>com.querydslgroupId>

            <artifactId>querydsl-jpaartifactId>

            <version>5.0.0version>

        dependency>

        
        <dependency>
            <groupId>com.querydslgroupId>

            <artifactId>querydsl-aptartifactId>

            <version>5.0.0version>

            <scope>providedscope>

        dependency>


        
        <dependency>
            <groupId>mysqlgroupId>

            <artifactId>mysql-connector-javaartifactId>

            <version>8.0.30version>

        dependency>

        
        <dependency>
            <groupId>io.swaggergroupId>

            <artifactId>swagger-annotationsartifactId>

            <version>1.5.21version>

        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>

            <artifactId>lombokartifactId>

        dependency>

如果集成queryDSL,需要在xml加入以下插件进行编译

     
        
            
                com.mysema.maven

                apt-maven-plugin

                1.1.3

                
                    
                        
                            process

                        

                        
                            target/generated-sources/queries

                            com.querydsl.apt.jpa.JPAAnnotationProcessor

                        

                    

                

            

        

    

application配置文件:

server:
  port: 8080
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/article
    username: root
    password: root

  jpa:
    generate-ddl: true
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        

持久层继承extends JpaRepository

你可能感兴趣的:(JAVA,Spring,spring,后端,java,开源)