SpringBoot jpa 注解 @ElementCollection 实现一对多关联

环境:

jdk: openjdk11

springboot: 2.2

操作系统: win10教育版 1903

1. pom.xml (jpa依赖主要在 jpa-redis注释下的两个)

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4     <modelVersion>4.0.0</modelVersion>
  5     <parent>
  6         <groupId>org.springframework.boot</groupId>
  7         <artifactId>spring-boot-starter-parent</artifactId>
  8         <version>2.2.4.RELEASE</version>
  9         <relativePath/> <!-- lookup parent from repository -->
 10     </parent>
 11     <groupId>com.rurjs</groupId>
 12     <artifactId>starter</artifactId>
 13     <version>0.0.1</version>
 14     <name>RurjsStarter</name>
 15     <description>Demo project for Spring Boot</description>
 16 
 17     <properties>
 18         <java.version>11</java.version>
 19         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 20         <security-jwt.version>1.0.9.RELEASE</security-jwt.version>
 21         <jjwt.version>0.9.0</jjwt.version>
 22         <spring-cloud-dependency-version>2.1.2.RELEASE</spring-cloud-dependency-version>
 23     </properties>
 24 
 25     <dependencies>
 26         <dependency>
 27             <groupId>org.springframework.boot</groupId>
 28             <artifactId>spring-boot-starter-web</artifactId>
 29         </dependency>
 30         <!--开发工具-->
 31         <dependency>
 32             <groupId>org.springframework.boot</groupId>
 33             <artifactId>spring-boot-devtools</artifactId>
 34             <scope>runtime</scope>
 35             <optional>true</optional>
 36         </dependency>
 37         <dependency>
 38             <groupId>org.apache.commons</groupId>
 39             <artifactId>commons-lang3</artifactId>
 40             <version>3.9</version>
 41         </dependency>
 42         <!--lombok工具-->
 43         <dependency>
 44             <groupId>org.projectlombok</groupId>
 45             <artifactId>lombok</artifactId>
 46             <optional>true</optional>
 47         </dependency>
 48         <!--单元测试-->
 49         <dependency>
 50             <groupId>org.springframework.boot</groupId>
 51             <artifactId>spring-boot-starter-test</artifactId>
 52             <scope>test</scope>
 53             <exclusions>
 54                 <exclusion>
 55                     <groupId>org.junit.vintage</groupId>
 56                     <artifactId>junit-vintage-engine</artifactId>
 57                 </exclusion>
 58             </exclusions>
 59         </dependency>
 60         <dependency>
 61             <groupId>io.projectreactor</groupId>
 62             <artifactId>reactor-test</artifactId>
 63             <scope>test</scope>
 64         </dependency>
 65         <!--jpa-redis-->
 66         <dependency>
 67             <groupId>org.springframework.boot</groupId>
 68             <artifactId>spring-boot-starter-data-jpa</artifactId>
 69         </dependency>
 70         <dependency>
 71             <groupId>mysql</groupId>
 72             <artifactId>mysql-connector-java</artifactId>
 73             <!--<version>5.1.46</version>-->
 74         </dependency>
 75         <dependency>
 76             <groupId>org.springframework.boot</groupId>
 77             <artifactId>spring-boot-starter-data-redis</artifactId>
 78         </dependency>
 79         <!--oauth2认证-->
 80         <dependency>
 81             <groupId>org..springframework.cloud</groupId>
 82             <artifactId>spring-cloud-starter-oauth2</artifactId>
 83             <version>${spring-cloud-dependency-version}</version>
 84         </dependency>
 85         <dependency>
 86             <groupId>org.springframework.cloud</groupId>
 87             <artifactId>spring-cloud-starter-security</artifactId>
 88             <version>${spring-cloud-dependency-version}</version>
 89         </dependency>
 90         <dependency>
 91             <groupId>org.springframework.security</groupId>
 92             <artifactId>spring-security-jwt</artifactId>
 93             <version>${security-jwt.version}</version>
 94         </dependency>
 95         <dependency>
 96             <groupId>io.jsonwebtoken</groupId>
 97             <artifactId>jjwt</artifactId>
 98             <version>0.7.0</version>
 99         </dependency>
100         <!--json-->
101         <dependency>
102             <groupId>com.alibaba</groupId>
103             <artifactId>fastjson</artifactId>
104             <version>1.2.57</version>
105         </dependency>
106         <!--API文档-springfox-swagger2-->
107         <dependency>
108             <groupId>io.springfox</groupId>
109             <artifactId>springfox-swagger2</artifactId>
110             <version>2.9.2</version>
111             <!--排除这个包,这里有个bug: Illegal DefaultValue  for parameter type integer-->
112             <exclusions>
113                 <exclusion>
114                     <groupId>io.swagger</groupId>
115                     <artifactId>swagger-models</artifactId>
116                 </exclusion>
117             </exclusions>
118         </dependency>
119         <dependency>
120             <groupId>io.swagger</groupId>
121             <artifactId>swagger-models</artifactId>
122             <version>1.5.21</version>
123         </dependency>
124         <dependency>
125             <groupId>io.springfox</groupId>
126             <artifactId>springfox-swagger-ui</artifactId>
127             <version>2.9.2</version>
128         </dependency>
129     </dependencies>
130 
131     <build>
132         <plugins>
133             <plugin>
134                 <groupId>org.springframework.boot</groupId>
135                 <artifactId>spring-boot-maven-plugin</artifactId>
136             </plugin>
137         </plugins>
138     </build>
139 
140 </project>

2. application.yml( 主要是 spring.datasource 和 spring.jpa部分)

 1 server:
 2   port: 9018
 3 spring:
 4   redis:
 5     host: 127.0.0.1
 6     database: 0
 7   datasource:
 8     url: jdbc:mysql://127.0.0.1:3306/rurjs_starter?characterEncoding=UTF-8&serverTimezone=UTC
 9     username: root
10     password: DRsXT5sJ6Oi55LPj
11   jpa:
12     hibernate:
13       ddl-auto: update
14     database: mysql
15     show-sql: true
16     database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
17 
18 rjsSecurity:
19   exclude:
20     antMatchers: /oauth/**,/login,/home
21 #logging:
22 #  level: debug

3. 主类

 1 package com.rurjs.starter;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.web.bind.annotation.RequestMapping;
 6 import org.springframework.web.bind.annotation.RestController;
 7 
 8 @SpringBootApplication
 9 public class RurjsStarterApplication {
10 
11     public static void main(String[] args) {
12         SpringApplication.run(RurjsStarterApplication.class, args);
13     }
14 
15     @RestController
16     public class HealthController{
17 
18         @RequestMapping("ping")
19         public String ping()
20         {
21             return "Pong";
22         }
23     }
24 }

4. 实体类

package com.rurjs.starter.config.sso.domain;


import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.provider.ClientDetails;
import java.io.Serializable;
import javax.persistence.*;
import java.util.*;
@Getter @Setter @ToString @Entity @EntityListeners(AuditingEntityListener.
class) @Table public class OauthClientDetails implements Serializable{ private static final long serialVersionUID = 2930534157595467437L; @Id private String clientId;//client_id @Fetch(FetchMode.JOIN) @ElementCollection private Set<String> resourceIds;//资源id private String clientSecret;//client 密钥 }

自动生成表如下:

mysql> show create table oauth_client_details_resource_ids;
+-----------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table                             | Create Table                                                                                                                                                                                                                                                                                                                                                                                               |
+-----------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| oauth_client_details_resource_ids | CREATE TABLE `oauth_client_details_resource_ids` (
  `oauth_client_details_client_id` varchar(255) NOT NULL,
  `resource_ids` varchar(255) DEFAULT NULL,
  KEY `FKo1c1xapbgjtw5kgq9c1fy8vp3` (`oauth_client_details_client_id`),
  CONSTRAINT `FKo1c1xapbgjtw5kgq9c1fy8vp3` FOREIGN KEY (`oauth_client_details_client_id`) REFERENCES `oauth_client_details` (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-----------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.03 sec)

mysql> 

5. Dao

package com.rurjs.starter.config.sso.dao;

import com.rurjs.starter.config.sso.domain.OauthClientDetails;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Collection;
@Repository("OauthClientDetailsDao")
public interface OauthClientDetailsDao extends JpaRepository<OauthClientDetails,String> {

    //批量删除.
    public int deleteAllByClientIdIn(Collection<String> clientId);
}

6. 单元测试

package com.rurjs.starter;

import com.rurjs.starter.config.sso.dao.OauthClientDetailsDao;
import com.rurjs.starter.config.sso.domain.OauthClientDetails;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@SpringBootTest
class RurjsStarterApplicationTests {

    @Autowired
    private OauthClientDetailsDao oauthClientDetailsDao;

    @Test
    public void ClientInsert()
    {
        Set<String> resources = new HashSet<>();
        resources.add("resource1");resources.add("all");

        OauthClientDetails oauthClientDetails = new OauthClientDetails();
        oauthClientDetails.setClientId("client1");
        oauthClientDetails.setClientSecret("123456");
        oauthClientDetails.setResourceIds(resources);

        OauthClientDetails oauthClientDetails1 = oauthClientDetailsDao.save(oauthClientDetails);

        System.out.println(oauthClientDetails1);
    }

    @Test
    public void ClientQuery()
    {
        String clientId = "client1";
        OauthClientDetails oauthClientDetails = oauthClientDetailsDao.findById(clientId).orElse(null);
        System.out.println(oauthClientDetails);
    }
}

7. 可能的一些问题及解决方式.

  1.  添加数据(带一对多关系)时正常,但是查询的时候抛出异常如下
    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.rurjs.starter.config.sso.domain.OauthClientDetails.resourceIds, could not initialize proxy - no Session
    
        at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
        at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
        at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:162)
        at org.hibernate.collection.internal.PersistentSet.size(PersistentSet.java:168)
        at java.base/java.util.HashSet.<init>(HashSet.java:119)
        at com.rurjs.starter.config.sso.domain.OauthClientDetails.getResourceIds(OauthClientDetails.java:66)
        at com.rurjs.starter.config.sso.domain.OauthClientDetails.toString(OauthClientDetails.java:18)
        at java.base/java.lang.String.valueOf(String.java:2951)
        at java.base/java.io.PrintStream.println(PrintStream.java:897)
        at com.rurjs.starter.RurjsStarterApplicationTests.ClientQuery(RurjsStarterApplicationTests.java:87)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
        at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
        at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)
        at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)
        at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)
        at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74)
        at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
        at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
        at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104)
        at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)
        at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)
        at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)
        at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
        at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
        at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
        at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
        at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
        at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
        at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
        at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
        at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
        at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
        at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
        at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
        at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:74)
        at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
        at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
        at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

    解决方式:在实体类中 给有一对多关系的属性(如 resourceIds )上添加注解 @Fetch(FetchMode.JOIN);无该注解和注解为 @Fetch(FetchMode.SUBSELECT)或 @Fetch(FetchMode.SELECT)时,都会抛出该异常,按异常信息来说,解决懒加载问题也行,题主未并未使用 解决懒加载的方式解决该异常。

原文地址:https://www.cnblogs.com/tu13/p/java_springboot_jpa_entity.html