H2基本使用 
pom 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <dependencies >         <dependency >              <groupId > org.springframework.boot</groupId >              <artifactId > spring-boot-starter-web</artifactId >          </dependency >          <dependency >              <groupId > org.projectlombok</groupId >              <artifactId > lombok</artifactId >              <optional > true</optional >          </dependency >          <dependency >              <groupId > org.springframework.boot</groupId >              <artifactId > spring-boot-starter-test</artifactId >              <scope > test</scope >              <exclusions >                  <exclusion >                      <groupId > org.junit.vintage</groupId >                      <artifactId > junit-vintage-engine</artifactId >                  </exclusion >              </exclusions >          </dependency >          <dependency >              <groupId > org.springframework.boot</groupId >              <artifactId > spring-boot-starter-data-jpa</artifactId >          </dependency >          <dependency >              <groupId > com.h2database</groupId >              <artifactId > h2</artifactId >              <scope > runtime</scope >          </dependency >      </dependencies >      <dependencyManagement >          <dependencies >              <dependency >                  <groupId > org.springframework.boot</groupId >                  <artifactId > spring-boot-dependencies</artifactId >                  <version > ${spring-boot.version}</version >                  <type > pom</type >                  <scope > import</scope >              </dependency >          </dependencies >      </dependencyManagement >  
 
application.properties 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 spring.application.name =dynamic-form server.port =8080 spring.datasource.url =jdbc:h2:file:./data/dynamic-form spring.datasource.driverClassName =org.h2.Driver spring.datasource.username =admin spring.datasource.password =123456 spring.jpa.database-platform =org.hibernate.dialect.H2Dialect spring.jpa.show-sql =true spring.jpa.hibernate.ddl-auto =update spring.jpa.properties.hibernate.use_sql_comments =true spring.jpa.properties.hibernate.format_sql =true spring.jpa.properties.hibernate.enable_lazy_load_no_trans =true spring.h2.console.enabled =true spring.h2.console.path =/h2-console spring.h2.console.settings.trace =false spring.h2.console.settings.web-allow-others =false logging.level.cn.idea360.dynamicform =debug 
 
entity 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package  com.example.odata.model;import  lombok.Data;import  javax.persistence.*;@Data @Table(name = "form") @Entity public  class  Form   {    @Id      @GeneratedValue      private  int  id;          @Column      private  String name; } 
 
orm 
 
1 2 3 4 5 6 7 8 package  com.example.odata.repository;import  com.example.odata.model.Form;import  org.springframework.data.jpa.repository.JpaRepository;import  org.springframework.stereotype.Repository;@Repository public  interface  FormRepository  extends  JpaRepository <Form , Integer >  {} 
 
junit5 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package  com.example.odata;import  com.example.odata.model.Form;import  com.example.odata.repository.FormRepository;import  lombok.extern.slf4j.Slf4j;import  org.junit.jupiter.api.DisplayName;import  org.junit.jupiter.api.Test;import  org.springframework.boot.test.context.SpringBootTest;import  javax.annotation.Resource;import  static  org.junit.jupiter.api.Assertions.assertEquals;@Slf4j @SpringBootTest @DisplayName("主表单单元测试") class  FormRepositoryTest   {    @Resource      private  FormRepository formRepository;     @DisplayName("新建表单")      @Test      void  addForm ()  throws  Exception  {         Form form = new  Form();         form.setName("动态表单" );         Form save = formRepository.save(form);         assertEquals(1 , save.getId());     }     @DisplayName("获取表单")      @Test      void  getData ()  throws  Exception  {         Form form = formRepository.getById(1 );         log.info("form: {}" , form);         assertEquals("动态表单" , form.getName());     } } 
 
  JPA简单说明 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 在JPA规范中,一对多的双向关系由多端来维护 @OneToMany:        作用:建立一对多的关系映射     属性:         targetEntityClass:指定多的多方的类的字节码         mappedBy:指定从表实体类中引用主表对象的名称。         cascade:指定要使用的级联操作         fetch:指定是否采用延迟加载         orphanRemoval:是否使用孤儿删除 @ManyToOne     作用:建立多对一的关系     属性:         targetEntityClass:指定一的一方实体类字节码         cascade:指定要使用的级联操作         fetch:指定是否采用延迟加载         optional:关联是否可选。如果设置为false,则必须始终存在非空关系。 @JoinColumn      作用:用于定义主键字段和外键字段的对应关系。      属性:         name:指定外键字段的名称         referencedColumnName:指定引用主表的主键字段名称         unique:是否唯一。默认值不唯一         nullable:是否允许为空。默认值允许。         insertable:是否允许插入。默认值允许。         updatable:是否允许更新。默认值允许。         columnDefinition:列的定义信息。 
 
基类 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package  cn.idea360.erp.entity;import  lombok.Data;import  lombok.experimental.Accessors;import  org.hibernate.annotations.CreationTimestamp;import  org.hibernate.annotations.UpdateTimestamp;import  org.springframework.data.jpa.domain.support.AuditingEntityListener;import  javax.persistence.*;import  java.io.Serializable;import  java.sql.Timestamp;@Data @Accessors(chain = true) @MappedSuperclass @EntityListeners(AuditingEntityListener.class) public  abstract  class  BaseTable  implements  Serializable   {    @Id      @GeneratedValue      @Column(name = "Id", nullable = false)      private  Integer id;     @CreationTimestamp      @Column(name = "CreateTime")      private  Timestamp createTime;     @UpdateTimestamp      @Column(name = "ChangeTime")      private  Timestamp changeTime;     @Column(name = "DeleteTime")      private  Timestamp deleteTime;     @Column(name = "IsDeleted", nullable = false)      private  Boolean isDeleted = false ;     @Column(name = "EmployeeId", nullable = false)      private  Integer employeeId; } 
 
一对多生成中间表 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package  com.example.odata.entity;import  lombok.Data;import  javax.persistence.*;import  java.util.List;@Data @Entity @Table(name="FATHER") public  class  Father   {    @Id      @GeneratedValue      private  Integer id;     private  String name;          @OneToMany(cascade = CascadeType.PERSIST)      private  List<Child> children; } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package  com.example.odata.entity;import  lombok.Data;import  javax.persistence.Entity;import  javax.persistence.GeneratedValue;import  javax.persistence.Id;import  javax.persistence.Table;@Data @Entity @Table(name="CHILD") public  class  Child   {    @Id      @GeneratedValue      private  Integer id;     private  String name; } 
 
1 2 3 4 5 6 7 8 9 10 11 12 package  com.example.odata.repository;import  com.example.odata.entity.Father;import  org.springframework.data.jpa.repository.JpaRepository;import  org.springframework.stereotype.Repository;@Repository public  interface  FatherRepository  extends  JpaRepository <Father , Integer >  {} 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package  com.example.odata;import  com.example.odata.entity.Child;import  com.example.odata.entity.Father;import  com.example.odata.repository.FatherRepository;import  com.fasterxml.jackson.databind.ObjectMapper;import  lombok.extern.slf4j.Slf4j;import  org.junit.jupiter.api.DisplayName;import  org.junit.jupiter.api.Test;import  org.springframework.boot.test.context.SpringBootTest;import  javax.annotation.Resource;import  java.util.Arrays;import  static  org.junit.jupiter.api.Assertions.assertEquals;@Slf4j @SpringBootTest @DisplayName("一对多单元测试, 会生成中间表FATHER_CHILDREN") public  class  FatherRepositoryTest   {    @Resource      private  FatherRepository fatherRepository;     @Resource      private  ObjectMapper objectMapper;     @DisplayName("插入Father")      @Test      void  addFather ()  throws  Exception  {         Father father = new  Father();         father.setName("父亲" );         Child child1 = new  Child();         child1.setName("大儿子" );         Child child2 = new  Child();         child2.setName("小女儿" );         father.setChildren(Arrays.asList(child1, child2));         Father save = fatherRepository.save(father);         log.info("father: {}" , objectMapper.writeValueAsString(save));         assertEquals("父亲" , save.getName());     }     @DisplayName("获取Father")      @Test      void  getFather ()  throws  Exception  {         Father father = fatherRepository.getById(1 );         log.info("father: {}" , father);         assertEquals("父亲" , father.getName());     } } 
 
sql执行过程
1 2 3 4 5 6 Hibernate: insert into father (name, id) values (?, ?) Hibernate: insert into child (name, id) values (?, ?) Hibernate: insert into child (name, id) values (?, ?) Hibernate: insert into father_children (father_id, children_id) values (?, ?) Hibernate: insert into father_children (father_id, children_id) values (?, ?) 2023-06-11 00:34:56.892  INFO 26519 --- [           main] com.example.odata.FatherRepositoryTest   : father: {"id":1,"name":"父亲","children":[{"id":2,"name":"大儿子"},{"id":3,"name":"小女儿"}]} 
 
可以看出是先添加一方, 再添加多方, 再建立中间表关联关系
一对多不生成中间表 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package  com.example.odata.entity;import  com.fasterxml.jackson.annotation.JsonIgnoreProperties;import  lombok.Data;import  javax.persistence.*;import  java.io.Serializable;import  java.util.List;@Data @Entity @Table(name="tb_user") @JsonIgnoreProperties(value = {"hibernateLazyInitializer"}) public  class  User  implements  Serializable   {    @Id      @GeneratedValue(strategy= GenerationType.IDENTITY)      @Column(name="id")      private  Integer id;     @Column(name="name")      private  String name;          @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)      @JoinColumn(name = "user_id", referencedColumnName = "id")      private  List<Todo> todos; } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package  com.example.odata.entity;import  lombok.Data;import  javax.persistence.*;import  java.io.Serializable;@Data @Entity @Table(name="tb_todo") public  class  Todo  implements  Serializable   {    @Id      @GeneratedValue(strategy= GenerationType.IDENTITY)      @Column(name="id")      private  Integer id;     @Column(name="task")      private  String task; } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package  com.example.odata;import  com.example.odata.entity.Todo;import  com.example.odata.entity.User;import  com.example.odata.repository.UserRepository;import  com.fasterxml.jackson.databind.ObjectMapper;import  lombok.extern.slf4j.Slf4j;import  org.junit.jupiter.api.DisplayName;import  org.junit.jupiter.api.Test;import  org.springframework.boot.test.context.SpringBootTest;import  javax.annotation.Resource;import  java.util.Arrays;@Slf4j @SpringBootTest @DisplayName("一对多单元测试, 不会生成中间表") public  class  UserRepositoryTest   {    @Resource      private  UserRepository userRepository;     @Resource      private  ObjectMapper objectMapper;     @DisplayName("插入User")      @Test      void  addUser ()  throws  Exception  {         Todo todo = new  Todo();         todo.setTask("完成JPA测试" );         User user = new  User();         user.setName("刘德华" );         user.setTodos(Arrays.asList(todo));         User save = userRepository.save(user);         log.info("user: {}" , objectMapper.writeValueAsString(save));     }     @DisplayName("查询User")      @Test      void  queryUser ()  throws  Exception  {         User user = userRepository.getById(1 );         log.info("user: {}" , objectMapper.writeValueAsString(user));     } } 
 
sql执行过程
1 2 3 4 5 6 Hibernate: insert into tb_user (id, name) values (default, ?) Hibernate: insert into tb_todo (id, task) values (default, ?) Hibernate: insert into tb_todo (id, task) values (default, ?) Hibernate: update tb_todo set user_id=? where id=? Hibernate: update tb_todo set user_id=? where id=? 2023-06-11 02:11:48.230  INFO 29231 --- [           main] com.example.odata.UserRepositoryTest     : user: {"id":1,"name":"刘德华","todos":[{"id":1,"task":"完成JPA测试"},{"id":2,"task":"完成JPA测试2"}]} 
 
一对多不生成中间表 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package  com.example.odata.entity;import  com.fasterxml.jackson.annotation.JsonManagedReference;import  lombok.Data;import  lombok.EqualsAndHashCode;import  javax.persistence.*;import  java.io.Serializable;import  java.util.Set;@Data @Entity @Table(name = "books") public  class  Book  implements  Serializable   {    @Id      @GeneratedValue(strategy = GenerationType.IDENTITY)      private  Integer id;     private  String title;     @EqualsAndHashCode .Exclude     @JsonManagedReference      @OneToMany(mappedBy = "book", fetch = FetchType.LAZY,              cascade = CascadeType.ALL)     private  Set<Page> pages; } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package  com.example.odata.entity;import  com.fasterxml.jackson.annotation.JsonBackReference;import  lombok.Data;import  javax.persistence.*;import  java.io.Serializable;@Data @Entity @Table(name = "pages") public  class  Page  implements  Serializable   {    @Id      @GeneratedValue(strategy = GenerationType.IDENTITY)      private  Integer id;     private  int  number;     private  String content;     @JsonBackReference      @ManyToOne(fetch = FetchType.LAZY, optional = false)      @JoinColumn(name = "book_id", nullable = false)      private  Book book; } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package  com.example.odata;import  com.example.odata.entity.Book;import  com.example.odata.entity.Page;import  com.example.odata.repository.BookRepository;import  com.example.odata.repository.PageRepository;import  com.fasterxml.jackson.databind.ObjectMapper;import  lombok.extern.slf4j.Slf4j;import  org.junit.jupiter.api.DisplayName;import  org.junit.jupiter.api.Test;import  org.springframework.boot.test.context.SpringBootTest;import  javax.annotation.Resource;@Slf4j @SpringBootTest @DisplayName("一对多单元测试, 不会生成中间表, 多端维护") public  class  BookRepositoryTest   {    @Resource      private  BookRepository bookRepository;     @Resource      private  PageRepository pageRepository;     @Resource      private  ObjectMapper objectMapper;     @DisplayName("插入Book和Page")      @Test      void  addBook ()  throws  Exception  {         Book book = new  Book();         book.setTitle("JPA双向关联" );         Book save = bookRepository.save(book);         log.info("book: {}" , objectMapper.writeValueAsString(save));         Page page1 = new  Page();         page1.setNumber(1 );         page1.setContent("第一页" );         page1.setBook(book);         Page page2 = new  Page();         page2.setNumber(2 );         page2.setContent("第二页" );         page2.setBook(book);         pageRepository.save(page1);         pageRepository.save(page2);         log.info("page1: {}" , objectMapper.writeValueAsString(page1));         log.info("page2: {}" , objectMapper.writeValueAsString(page2));     }     @DisplayName("查询Book和Page")      @Test      void  getBook ()  throws  Exception  {         Book book = bookRepository.findById(1 ).get();         log.info("book: {}" , objectMapper.writeValueAsString(book));         Page page = pageRepository.findById(2 ).get();         log.info("page: {}" , objectMapper.writeValueAsString(page));     } } 
 
参考