springData JPA 的JPA关系查询

我爱海鲸 2024-11-02 19:11:48 暂无标签

简介jpa、hibernate、springboot

1、全局的pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>xyz.haijin</groupId>
    <artifactId>springDataTest</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>jpa_hibernate_01</module>
        <module>springdata_jpa_02</module>
        <module>springdata_jpa_03</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <querydsl.version>4.4.0</querydsl.version>
        <apt.version>1.1.3</apt.version>
        <mysql.version>8.0.29</mysql.version>
    </properties>


    <!--统一管理SpringData子项目的版本-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-bom</artifactId>
                <version>2021.1.0</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>



</project>

2、项目的pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springDataTest</artifactId>
        <groupId>xyz.haijin</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springdata_jpa_03</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
        </dependency>

        <!-- junit4 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
        <!-- hibernate对jpa的支持包 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.32.Final</version>
        </dependency>

        <!-- Mysql and MariaDB -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>

        <!--spring-test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.10</version>
            <scope>test</scope>
        </dependency>


        <!--spring-test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.10</version>
            <scope>test</scope>
        </dependency>

        <!-- querydsl -->
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
            <version>${querydsl.version}</version>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>com.mysema.maven</groupId>
                <artifactId>apt-maven-plugin</artifactId>
                <version>${apt.version}</version>
                <dependencies>
                    <dependency>
                        <groupId>com.querydsl</groupId>
                        <artifactId>querydsl-apt</artifactId>
                        <version>${querydsl.version}</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>target/generated-sources/queries</outputDirectory>
                            <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
                            <logOnlyOnError>true</logOnlyOnError>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

3、SpringDataJPAConfig:

package xyz.haijin.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
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;

/**
 * jpa配置
 * <p>
 * 2024/11/2.
 *
 * @author haijin
 * @version v1
 */
//@Configuration          // 标记当前类为配置类   =xml配文件
@EnableJpaRepositories(basePackages="xyz.haijin.repositories")  // 启动jpa    <jpa:repositories
@EnableTransactionManagement    // 开启事务
public class SpringDataJPAConfig {

    /*
    *  <!--数据源-->
    <bean class="com.alibaba.druid.pool.DruidDataSource" name="dataSource">
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/springdata_jpa?characterEncoding=UTF-8"/>
    </bean>
    * */
    @Bean
    public DataSource dataSource() {

        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("haijinmysql");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://192.168.3.10:3306/springdata_jpa?characterEncoding=UTF-8");

        return  dataSource;

    }

    /*
    *  <!--EntityManagerFactory-->
    <bean name="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="jpaVendorAdapter">
            <!--Hibernate实现-->
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!--生成数据库表-->
                <property name="generateDdl" value="true"></property>
                <property name="showSql" value="true"></property>
            </bean>
        </property>
        <!--设置实体类的包-->
        <property name="packagesToScan" value="com.tuling.pojo"></property>
        <property name="dataSource" ref="dataSource" ></property>
    </bean>
    * */
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(true);
        vendorAdapter.setShowSql(true);

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPackagesToScan("xyz.haijin.po");
        factory.setDataSource(dataSource());
        return factory;
    }

    /*
    * <bean class="org.springframework.orm.jpa.JpaTransactionManager" name="transactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"></property>
    </bean>
    * */
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {

        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory);
        return txManager;
    }
}

4、Account:

package xyz.haijin.po;

import lombok.Data;

import javax.persistence.*;

/***
 *
 * 一对一
 * 一个客户对一个账户
 */
@Entity
@Table(name="tb_account")
@Data
/*@Getter         //  生成所有属性的get方法
@Setter        //  生成所有属性的set方法
@RequiredArgsConstructor  //  生成final属性的构造函数, 如果没有final就是无参构造函数
@EqualsAndHashCode*/
public class Account {

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

    @OneToOne
    @JoinColumn(name="customer_id")
    private Customer customer;

}

5、Customer:

package xyz.haijin.po;

import lombok.Data;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.util.Date;
import java.util.List;

@Entity     // 作为hibernate 实体类
@Table(name = "tb_customer")       // 映射的表明
@Data
@EntityListeners(AuditingEntityListener.class)
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long custId; //客户的主键

    @Column(name = "cust_name")
    private String custName;//客户名称

    @Column(name="cust_address")
    private String custAddress;//客户地址


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


    // 一对多
    // fetch 默认是懒加载   懒加载的优点( 提高查询性能)
    @OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
    @JoinColumn(name="customer_id")
    private List<Message> messages;

//    @Override
//    public String toString() {
//        return "Customer{" +
//                "custId=" + custId +
//                ", custName='" + custName + '\'' +
//                ", custAddress='" + custAddress + '\'' +
//                ", account=" + account +
//                ", messages=" + messages.toString() +   // 会用到懒加载的数据, 用到的时候就会执行懒加载查询
//                '}';
//    }

    // 单向多对多
    @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;

    private @Version Long version;


    @CreatedBy
    String createdBy;

    @LastModifiedBy
    String modifiedBy;
    /**
     * 实体创建时间
     */
    @Temporal(TemporalType.TIMESTAMP)
    @CreatedDate
    protected Date dateCreated = new Date();

    /**
     * 实体修改时间
     */
    @Temporal(TemporalType.TIMESTAMP)
    @LastModifiedDate
    protected Date dateModified = new Date();
}

6、Message:

package xyz.haijin.po;

import lombok.Data;

import javax.persistence.*;

/***
 * 一(客户)对多(信息)
 */
@Entity
@Table(name="tb_message")
@Data
public class Message {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String info;

    public Message(String info) {
        this.info = info;
    }

    public Message(String info, Customer customer) {
        this.info = info;
        this.customer = customer;
    }

    // 一定要有、否则查询就会有问题
    public Message() {
    }

    // 多对一
    @ManyToOne(cascade = {CascadeType.PERSIST,CascadeType.REMOVE})
    @JoinColumn(name="customer_id")
    private Customer customer;

    @Override
    public String toString() {
        return "Message{" +
                "id=" + id +
                ", info='" + info + '\'' +
                ", customerId=" + customer.getCustId() +
                ", customerName=" + customer.getCustName() +
                '}';
    }
}

7、Role:

package xyz.haijin.po;

import lombok.Data;

import javax.persistence.*;
import java.util.List;

/***
 *
 * 多(用户)对多(角色)
 */
@Entity
@Table(name="tb_role")
@Data
public class Role {

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

    @Column(name="role_name")
    private String rName;

    public Role(String rName) {
        this.rName = rName;
    }

    public Role(Long id, String rName) {
        this.id = id;
        this.rName = rName;
    }

    public Role() {
    }


    @ManyToMany(cascade = CascadeType.ALL)
    private List<Role> roles;
}

8、CustomerRepository:

package xyz.haijin.repositories;

import org.springframework.data.repository.PagingAndSortingRepository;
import xyz.haijin.po.Customer;


public interface CustomerRepository extends PagingAndSortingRepository<Customer,Long>{

}

9、MessageRepository:

package xyz.haijin.repositories;

import org.springframework.data.repository.PagingAndSortingRepository;
import xyz.haijin.po.Customer;
import xyz.haijin.po.Message;

import java.util.List;


public interface MessageRepository extends PagingAndSortingRepository<Message,Long>{

    // 根据客户id查询所有信息
    // 通过规定方法名来实现关联查询: 需要通过关联属性来进行匹配
    // 但是只能通过id来进行匹配
    List<Message> findByCustomer(Customer customer);

}

10、RoleRepository:

package xyz.haijin.repositories;

import org.springframework.data.repository.PagingAndSortingRepository;
import xyz.haijin.po.Role;


public interface RoleRepository extends PagingAndSortingRepository<Role,Long>{

}

11、ManyToManyTest:

package xyz.haijin;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
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 xyz.haijin.config.SpringDataJPAConfig;
import xyz.haijin.po.Customer;
import xyz.haijin.po.Role;
import xyz.haijin.repositories.CustomerRepository;
import xyz.haijin.repositories.RoleRepository;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;


@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ManyToManyTest {
    @Autowired
    CustomerRepository repository;
    @Autowired
    RoleRepository roleRepository;

    // 保存
    /*
    1.如果保存的关联数据  希望使用已有的 ,就需要从数据库中查出来(持久状态)。否则 提示 游离状态不能持久化
    2.如果一个业务方法有多个持久化操作, 记得加上@Transactional ,否则不能共用一个session
    3. 在单元测试中用到了@Transactional , 如果有增删改的操作一定要加@Commit
    4. 单元测试会认为你的事务方法@Transactional,  只是测试而已, 它不会为你提交事务,  需要单独加上 @Commit
     */
    @Test
    @Transactional
    @Commit
    public void testC() {

        List<Role> roles=new ArrayList<>();
        roles.add(roleRepository.findById(9L).get());
        roles.add(roleRepository.findById(10L).get());

        Customer customer = new Customer();
        customer.setCustName("诸葛");
        customer.setRoles(roles);

        repository.save(customer);
    }


    @Test
    @Transactional(readOnly = true)
    public void testR() {


        System.out.println(repository.findById(14L));

        //repository.save(customer);
    }


    /*
    * 注意加上
    * @Transactional
        @Commit
      多对多其实不适合删除, 因为经常出现数据出现可能除了和当前这端关联还会关联另一端,此时删除就会: ConstraintViolationException。
      * 要删除, 要保证没有额外其他另一端数据关联
    * */
    @Test
    @Transactional
    @Commit
    public void testD() {


        Optional<Customer> customer = repository.findById(14L);

        repository.delete(customer.get());
    }
}

12、ManyToOneTest:

package xyz.haijin;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import xyz.haijin.config.SpringDataJPAConfig;
import xyz.haijin.po.Customer;
import xyz.haijin.po.Message;
import xyz.haijin.repositories.MessageRepository;

import java.util.ArrayList;
import java.util.List;

@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ManyToOneTest {
    @Autowired
    MessageRepository repository;

    // 多对一 插入
    // 得出: 当插入"多"的数据的时候,使用多对一的关联关系是更加合理
    @Test
    public void testC(){
        // 一
        Customer customer = new Customer();
        customer.setCustName("司马懿");

        // 多
        List<Message> list=new ArrayList<>();
        list.add(new Message("你好",customer));
        list.add(new Message("在吗?",customer));


        repository.saveAll(list);
    }


    // 多对一:根据客户id查询对应的所有信息
    // 通过“一”进行条件查询, 在一对多中实现是更合理的
    @Test
    public void testR(){
        Customer customer = new Customer();
        customer.setCustId(1L);
        customer.setCustName("xxx");

        List<Message> messages = repository.findByCustomer(customer);
        // 隐式调用toString()
        System.out.println(messages);
    }


    @Test
    public void testD(){
        Customer customer = new Customer();
        customer.setCustId(1L);

        List<Message> messages = repository.findByCustomer(customer);


        repository.deleteAll(messages);
    }
}

13、OneToManyTest:

package xyz.haijin;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
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 xyz.haijin.config.SpringDataJPAConfig;
import xyz.haijin.po.Customer;
import xyz.haijin.po.Message;
import xyz.haijin.repositories.CustomerRepository;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;


@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class OneToManyTest {
    @Autowired
    CustomerRepository repository;

    // 插入
    @Test
    public void testC(){

        List<Message> messageList=new ArrayList<>();
        messageList.add(new Message("您好"));
        messageList.add(new Message("在吗?"));

        Customer customer = new Customer();
        customer.setCustName("徐庶帅哥");
        customer.setMessages(messageList);

        repository.save(customer);

    }


    // 插入
    @Test
    @Transactional(readOnly = true)
    public void testR(){
        // 懒加载过程:
        // 1.findById  只会查询Customer 和其他关联的立即加载
        Optional<Customer> customer = repository.findById(1L);
        System.out.println("=====================");
        // 由于输出, 会自动调用customer.toString()
        System.out.println(customer);


    }


    // 插入
    @Test
    public void testD(){

         repository.deleteById(2L);

    }



    @Test
    @Transactional
    @Commit
    public void testU(){

        Optional<Customer> customer = repository.findById(19L);
        customer.get().setCustName("xxx");

    }
}

14、OneToOneTest:

package xyz.haijin;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import xyz.haijin.config.SpringDataJPAConfig;
import xyz.haijin.po.Account;
import xyz.haijin.po.Customer;
import xyz.haijin.repositories.CustomerRepository;

import java.util.Optional;


@ContextConfiguration(classes = SpringDataJPAConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class OneToOneTest {
    @Autowired
    CustomerRepository repository;

    // 插入
    @Test
    public void testC(){
        // 初始化数据
        Account account = new Account();
        account.setUsername("xushu");

        Customer customer = new Customer();
        customer.setCustName("haijin");
        customer.setAccount(account);

        account.setCustomer(customer);

        repository.save(customer);
    }


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


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



    @Test
    public void testU(){

        Customer customer = new Customer();
        customer.setCustId(16L);
        customer.setCustName("haijin");
        customer.setAccount(null);
        repository.save(customer);
    }


}

你好:我的2025