首页 > 解决方案 > 多对多实现错误: id 列不允许 Null

问题描述

我正在尝试实现多对多关系,并且在实现时我一直为 id 列保持自动生成,它给了我错误

“SERVICE_ID”列不允许为 NULL;SQL 语句:

因此,为了避免这种情况,我尝试在保存商家实体的同时手动提供 ID,但仍然出现相同的错误。

示例代码片段:

商家实体

@Entity
@Table(name = "service")
@Getter 
@Setter 
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MerchantEntity {

    @Id
    @Column(name = "merchant_id")
    private Integer merchantId;
    
    @Column(name = "merchant_name")
    private String merchantName;
    
    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "merchant_service_mapping",
                joinColumns = @JoinColumn(name = "merchant_id", referencedColumnName = "merchant_id"),
                inverseJoinColumns = @JoinColumn(name = "service_id", referencedColumnName = "service_id"))
    private  List<ServiceEntity> merchantService;
}

服务实体

@Entity
@Table(name = "service")
@Getter 
@Setter 
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ServiceEntity {

    @Id
    @Column(name = "service_id")
    private Integer serviceId;
    
    @Column(name = "service_name")
    private String serviceName;
    
    @ManyToMany(mappedBy = "merchantService")
    private List<MerchantEntity> merchants;
    
}

测试方法

@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class EntityMappingApplicationTests {



    @Autowired
    private MerchantRepository merchantRepository;
    
    @Autowired
    private ServiceRepository serviceRepository;

    private ServiceEntity serviceEntity1 = null;
    private ServiceEntity serviceEntity2 = null;
    private ServiceEntity serviceEntity3 = null;
    private MerchantEntity merchantEntity1 = null;
    private MerchantEntity merchantEntity2 = null;

@BeforeAll
    public void setup() {
        
        serviceEntity1 = ServiceEntity.builder()
                                        .serviceId(1)
                                        .serviceName("Plumbing")
                                        .build();
        serviceEntity2 = ServiceEntity.builder()
                                        .serviceId(2)
                                        .serviceName("Home Renovation")
                                        .build();
        
        serviceEntity3 = ServiceEntity.builder()
                                        .serviceId(3)
                                        .serviceName("Carpenting")
                                        .build();

        
        List<ServiceEntity> serviceList1 = new ArrayList<>();
        serviceList1.add(serviceEntity1);
        serviceList1.add(serviceEntity2);
        
        List<ServiceEntity> serviceList2 = new ArrayList<>();
        serviceList2.add(serviceEntity1);
        serviceList2.add(serviceEntity3);
        
        
        merchantEntity1 = MerchantEntity.builder()
                                            .merchantId(1)
                                            .merchantName("John's Builder")
                                            .merchantService(serviceList1)
                                            .build();
        
        merchantEntity1 = MerchantEntity.builder()
                                            .merchantId(2)
                                            .merchantName("Jack Repairs")
                                            .merchantService(serviceList2)
                                            .build();
        
        merchantEntity1 = merchantRepository.save(merchantEntity1);
        //merchantId1 = merchantEntity1.getMerchantId();
        
        merchantEntity2 = merchantRepository.save(merchantEntity2);
        //merchantId2 = merchantEntity2.getMerchantId();
        
        
    }

    
    
    @Test
    public void testManyToMany() {

        MerchantEntity merchant1 = merchantRepository.findById(merchantId1).orElse(null);

        assertNotNull(merchant1.getMerchantService());
        assertEquals("Plumbing", merchant1.getMerchantService().get(0).getServiceName());
        assertEquals("Home Renovation", merchant1.getMerchantService().get(1).getServiceName());
        
        MerchantEntity merchant2 = merchantRepository.findById(merchantId2).orElse(null);

        assertNotNull(merchant2.getMerchantService());
        assertEquals("Plumbing", merchant2.getMerchantService().get(0).getServiceName());
        assertEquals("Carpenting", merchant2.getMerchantService().get(1).getServiceName());
        
        
        ServiceEntity serviceEntity1 = serviceRepository.findByServiceName("Plumbing");
        assertNotNull(serviceEntity1.getMerchants());
        assertEquals("John's Builder", serviceEntity1.getMerchants().get(0));
        assertEquals("Jack Repairs", serviceEntity1.getMerchants().get(1));
        
        ServiceEntity serviceEntity2 = serviceRepository.findByServiceName("Plumbing");
        assertNotNull(serviceEntity2.getMerchants());
        assertEquals("John's Builder", serviceEntity2.getMerchants().get(0));
        assertEquals("Jack Repairs", serviceEntity2.getMerchants().get(1));
    }
}

控制台日志

20:47:05.092 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
20:47:05.104 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
20:47:05.125 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.shail.entityMapping.EntityMappingApplicationTests] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper]
20:47:05.135 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.shail.entityMapping.EntityMappingApplicationTests], using SpringBootContextLoader
20:47:05.139 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.shail.entityMapping.EntityMappingApplicationTests]: class path resource [com/shail/entityMapping/EntityMappingApplicationTests-context.xml] does not exist
20:47:05.139 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.shail.entityMapping.EntityMappingApplicationTests]: class path resource [com/shail/entityMapping/EntityMappingApplicationTestsContext.groovy] does not exist
20:47:05.140 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.shail.entityMapping.EntityMappingApplicationTests]: no resource found for suffixes {-context.xml, Context.groovy}.
20:47:05.140 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [com.shail.entityMapping.EntityMappingApplicationTests]: EntityMappingApplicationTests does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
20:47:05.184 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.shail.entityMapping.EntityMappingApplicationTests]
20:47:05.242 [main] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [C:\Users\ssing\Desktop\entityMapping\target\classes\com\shail\entityMapping\EntityMappingApplication.class]
20:47:05.243 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration com.shail.entityMapping.EntityMappingApplication for test class com.shail.entityMapping.EntityMappingApplicationTests
20:47:05.376 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - @TestExecutionListeners is not present for class [com.shail.entityMapping.EntityMappingApplicationTests]: using defaults.
20:47:05.377 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.event.ApplicationEventsTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
20:47:05.388 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Skipping candidate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener] due to a missing dependency. Specify custom listener classes or make the default listener classes and their required dependencies available. Offending class: [javax/servlet/ServletContext]
20:47:05.397 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@626c44e7, org.springframework.test.context.event.ApplicationEventsTestExecutionListener@4dc8caa7, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@1d730606, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@3bcbb589, org.springframework.test.context.support.DirtiesContextTestExecutionListener@3b00856b, org.springframework.test.context.transaction.TransactionalTestExecutionListener@3016fd5e, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@35d08e6c, org.springframework.test.context.event.EventPublishingTestExecutionListener@53d102a2, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@6c45ee6e, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@6b3e12b5, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@5aac4250, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@1338fb5, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@42463763, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener@59f63e24]
20:47:05.404 [main] DEBUG org.springframework.test.context.support.DependencyInjectionTestExecutionListener - Performing dependency injection for test context [[DefaultTestContext@674bd420 testClass = EntityMappingApplicationTests, testInstance = com.shail.entityMapping.EntityMappingApplicationTests@2b0f373b, testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2ceb80a1 testClass = EntityMappingApplicationTests, locations = '{}', classes = '{class com.shail.entityMapping.EntityMappingApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@66ea810, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@2254127a, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2d29b4ee, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@13d73f29, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@4ee203eb, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@2b6856dd], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]].
20:47:05.436 [main] DEBUG org.springframework.test.context.support.TestPropertySourceUtils - Adding inlined properties to environment: {spring.jmx.enabled=false, org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.4)

2021-09-19 20:47:05.763  INFO 10216 --- [           main] c.s.e.EntityMappingApplicationTests      : Starting EntityMappingApplicationTests using Java 16.0.2 on DESKTOP-3GKR7OI with PID 10216 (started by ssing in C:\Users\ssing\Desktop\entityMapping)
2021-09-19 20:47:05.766  INFO 10216 --- [           main] c.s.e.EntityMappingApplicationTests      : No active profile set, falling back to default profiles: default
2021-09-19 20:47:06.252  INFO 10216 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-09-19 20:47:06.312  INFO 10216 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 51 ms. Found 4 JPA repository interfaces.
2021-09-19 20:47:06.887  INFO 10216 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-09-19 20:47:07.085  INFO 10216 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2021-09-19 20:47:07.168  INFO 10216 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2021-09-19 20:47:07.252  INFO 10216 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.32.Final
2021-09-19 20:47:07.418  INFO 10216 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2021-09-19 20:47:07.560  INFO 10216 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
Hibernate: drop table if exists card CASCADE 
Hibernate: drop table if exists merchant_service_mapping CASCADE 
Hibernate: drop table if exists passport CASCADE 
Hibernate: drop table if exists person CASCADE 
Hibernate: drop table if exists person_card_mapping CASCADE 
Hibernate: drop table if exists service CASCADE 
Hibernate: drop sequence if exists hibernate_sequence
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table card (card_id integer not null, card_no varchar(255), cardholder_name varchar(255), expiry_month varchar(255), expiry_year varchar(255), primary key (card_id))
Hibernate: create table merchant_service_mapping (merchant_id integer not null, service_id integer not null)
Hibernate: create table passport (passport_id integer not null, country varchar(255), passport_number varchar(255), passport_type varchar(255), primary key (passport_id))
Hibernate: create table person (person_id integer not null, person_name varchar(255), passport_number integer, primary key (person_id))
Hibernate: create table person_card_mapping (person_id integer not null, card_id integer not null)
Hibernate: create table service (merchant_id integer not null, merchant_name varchar(255), service_id integer not null, service_name varchar(255), primary key (service_id))
Hibernate: alter table person add constraint UK_9tt3moyr2yhcaddgdp1was853 unique (passport_number)
Hibernate: alter table person_card_mapping add constraint UK_nxru3hj8t5ywdcjs3ovd2s691 unique (card_id)
Hibernate: alter table merchant_service_mapping add constraint FKbuhbjw9k0whs5ll5js1b39rpu foreign key (service_id) references service
Hibernate: alter table merchant_service_mapping add constraint FKe0ovuw4u7kolir902qgticayl foreign key (merchant_id) references service
Hibernate: alter table person add constraint FKo5flbgdshfp73k9hdhllj09ec foreign key (passport_number) references passport
Hibernate: alter table person_card_mapping add constraint FKg1knlname54gx6g7h9uu0nm8q foreign key (card_id) references card
Hibernate: alter table person_card_mapping add constraint FK72a27v5da41ycmxlhs43pn2oy foreign key (person_id) references person
2021-09-19 20:47:08.301  INFO 10216 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2021-09-19 20:47:08.310  INFO 10216 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2021-09-19 20:47:08.947  INFO 10216 --- [           main] c.s.e.EntityMappingApplicationTests      : Started EntityMappingApplicationTests in 3.507 seconds (JVM running for 4.558)
Hibernate: select merchanten0_.merchant_id as merchant1_5_1_, merchanten0_.merchant_name as merchant2_5_1_, merchantse1_.merchant_id as merchant1_1_3_, serviceent2_.service_id as service_2_1_3_, serviceent2_.service_id as service_3_5_0_, serviceent2_.service_name as service_4_5_0_ from service merchanten0_ left outer join merchant_service_mapping merchantse1_ on merchanten0_.merchant_id=merchantse1_.merchant_id left outer join service serviceent2_ on merchantse1_.service_id=serviceent2_.service_id where merchanten0_.merchant_id=?
Hibernate: select serviceent0_.service_id as service_3_5_0_, serviceent0_.service_name as service_4_5_0_ from service serviceent0_ where serviceent0_.service_id=?
Hibernate: select serviceent0_.service_id as service_3_5_0_, serviceent0_.service_name as service_4_5_0_ from service serviceent0_ where serviceent0_.service_id=?
Hibernate: insert into service (merchant_name, merchant_id) values (?, ?)
2021-09-19 20:47:09.075  WARN 10216 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 23502, SQLState: 23502
2021-09-19 20:47:09.075 ERROR 10216 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : NULL not allowed for column "SERVICE_ID"; SQL statement:
insert into service (merchant_name, merchant_id) values (?, ?) [23502-200]
2021-09-19 20:47:09.104  INFO 10216 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2021-09-19 20:47:09.105  INFO 10216 --- [ionShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
Hibernate: drop table if exists card CASCADE 
Hibernate: drop table if exists merchant_service_mapping CASCADE 
Hibernate: drop table if exists passport CASCADE 
Hibernate: drop table if exists person CASCADE 
Hibernate: drop table if exists person_card_mapping CASCADE 
Hibernate: drop table if exists service CASCADE 
Hibernate: drop sequence if exists hibernate_sequence
2021-09-19 20:47:09.117  INFO 10216 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2021-09-19 20:47:09.120  INFO 10216 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

标签: javahibernatespring-data-jpa

解决方案


推荐阅读