SpringBoot JPA 定义实体类关系:一对一 (增删改查)

tech2025-10-21  12

SpringBoot 项目整体结构:

pom.xml 文件依赖:

<!-- 版本集中管理 --> <properties> <javax.servlet-api.version>3.1.0</javax.servlet-api.version> <mysql-connector-java.version>8.0.11</mysql-connector-java.version> <druid-spring-boot-starter.version>1.1.9</druid-spring-boot-starter.version> <commons-lang.version>2.6</commons-lang.version> <commons-codec.version>1.10</commons-codec.version> <commons-lang3.version>3.9</commons-lang3.version> <commons-net.version>3.6</commons-net.version> <commons-io.version>2.6</commons-io.version> <commons-collections.version>3.2.1</commons-collections.version> <commons-text.version>1.8</commons-text.version> <common-fileupload.version>1.3.1</common-fileupload.version> </properties> <dependencies> <!--springboot web 基础模块 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- springboot test 测试模块 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- spring-data-jpa --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> <version>1.13.22.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>4.3.21.RELEASE</version> </dependency> <!--servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${javax.servlet-api.version}</version> </dependency> <!-- mysql 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql-connector-java.version}</version> <scope>runtime</scope> </dependency> <!--数据库连接池druid 模块 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid-spring-boot-starter.version}</version> </dependency> <!-- lombok让代码更简化 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.16</version> <scope>provided</scope> </dependency> <!-- Swagger2 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency> <!-- fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.48</version> </dependency> <!-- apache common 工具包 --> <!--common-lang 常用工具包 --> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>${commons-lang.version}</version> </dependency> <!--commons-lang3 工具包 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons-lang3.version}</version> </dependency> <!--commons-codec 加密工具包 --> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>${commons-codec.version}</version> </dependency> <!--commons-net 网络工具包 --> <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>${commons-net.version}</version> </dependency> <!--common-io 工具包 --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>${commons-io.version}</version> </dependency> <!--common-collection 工具包 --> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>${commons-collections.version}</version> </dependency> <!--common-fileupload 工具包 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>${common-fileupload.version}</version> </dependency> <!-- common-text 工具包 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-text</artifactId> <version>${commons-text.version}</version> </dependency> </dependencies>

资源文件夹(resource)定义:

application.properties:

# 指定服务端口 server.port=9098 #数据库配置 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/booksalestore?serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true&allowMultiQueries=true&nullCatalogMeansCurrent=true spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #JPA 配置 # 格式化sql语句 spring.jpa.properties.hibernate-format_sql=true # 控制台展示 JPA 框架生成的sql语句 spring.jpa.show-sql=true # Druid 配置 # 初始化时建立物理连接的个数 spring.datasource.druid.initial-size=5 # 最大连接池数量 spring.datasource.druid.max-active=30 # 最小连接池数量 spring.datasource.druid.min-idle=5 # 获取连接时最大等待时间,单位毫秒 spring.datasource.druid.max-wait=60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 spring.datasource.druid.time-between-eviction-runs-millis=60000 # 连接保持空闲而不被驱逐的最小时间 spring.datasource.druid.min-evictable-idle-time-millis=300000 # 用来检测连接是否有效的sql,要求是一个查询语句 spring.datasource.druid.validation-query=SELECT 1 FROM DUAL # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 spring.datasource.druid.test-while-idle=true # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 spring.datasource.druid.test-on-borrow=false # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 spring.datasource.druid.test-on-return=false # 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 spring.datasource.druid.pool-prepared-statements=true # 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。 spring.datasource.druid.max-pool-prepared-statement-per-connection-size=50 # 配置监控统计拦截的filters,去掉后监控界面sql无法统计 #spring.datasource.druid.filters=stat,wall # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 # 合并多个DruidDataSource的监控数据 spring.datasource.druid.use-global-data-source-stat=true # 配置sql 注入方式 spring.datasource.druid.filters=stat,log4j #日志文件配置 logging.config=classpath:logback.xml

 logback.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL --> <!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 --> <!-- 以下 每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志--> <!-- 属性描述 scan:性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位, 默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> <configuration scan="true" scanPeriod="60 seconds" debug="false"> <!-- 定义日志文件 输入位置 --> <property name="log_dir" value="/logs/jpa" /> <!-- 日志最大的历史 30天 --> <property name="maxHistory" value="30"/> <!-- ConsoleAppender 控制台输出日志 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <!-- 对日志进行格式化 --> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern> </encoder> </appender> <!-- ERROR级别日志 --> <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender--> <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 过滤器,只记录WARN级别的日志 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <!-- 最常用的滚动策略,它根据时间来制定滚动策略.既负责滚动也负责出发滚动 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日志输出位置 可相对、和绝对路径 --> <fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/neo4j-error-log.log</fileNamePattern> <!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件假设设置每个月滚动,且<maxHistory>是6, 则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除--> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <!-- 按照固定窗口模式生成日志文件,当文件大于20MB时,生成新的日志文件。窗口大小是1到3,当保存了3个归档文件后,将覆盖最早的日志。 <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/.log.zip</fileNamePattern> <minIndex>1</minIndex> <maxIndex>3</maxIndex> </rollingPolicy> --> <!-- 查看当前活动文件的大小,如果超过指定大小会告知RollingFileAppender 触发当前活动文件滚动 <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <maxFileSize>5MB</maxFileSize> </triggeringPolicy> --> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern> </encoder> </appender> <!-- WARN级别日志 appender --> <appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 过滤器,只记录WARN级别的日志 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>WARN</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/neo4j-warn-log.log </fileNamePattern> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern> </encoder> </appender> <!-- INFO级别日志 appender --> <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 过滤器,只记录INFO级别的日志 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/neo4j-info-log.log </fileNamePattern> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern> </encoder> </appender> <!-- DEBUG级别日志 appender --> <appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>DEBUG</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/neo4j-debug-log.log </fileNamePattern> <maxHistory>${maxHistory}</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern> </encoder> </appender> <logger name="java.sql.PreparedStatement" value="DEBUG" /> <logger name="java.sql.Connection" value="DEBUG" /> <logger name="java.sql.Statement" value="DEBUG" /> <logger name="com.ibatis" value="DEBUG" /> <logger name="com.ibatis.common.jdbc.SimpleDataSource" value="DEBUG" /> <logger name="com.ibatis.common.jdbc.ScriptRunner" level="DEBUG"/> <logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate" value="DEBUG" /> <logger name="org.springframework.web" level="DEBUG"/> <logger name="com.digipower" level="DEBUG"/> <!-- root级别 DEBUG --> <root level="ERROR"> <!-- 控制台输出 --> <appender-ref ref="STDOUT" /> <!-- 文件输出 --> <appender-ref ref="ERROR" /> <appender-ref ref="INFO" /> <appender-ref ref="WARN" /> <appender-ref ref="DEBUG" /> </root> </configuration>

源码文件夹(src)定义

程序入口:

package com.zzg; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.support.SpringBootServletInitializer; @SpringBootApplication public class Application extends SpringBootServletInitializer { public static void main(String[] args) { // TODO Auto-generated method stub SpringApplication.run(Application.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { // 注意这里要指向原先用main方法执行的Application启动类 return builder.sources(Application.class); } }

系统配置

package com.zzg.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** * SpringMVC 配置对象 * @author zzg * */ @Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { //保存上传文件的目录 public final static String FILE_DIR="C:/BookSaleStore/upload/"; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // TODO Auto-generated method stub registry.addResourceHandler("/upload/**").addResourceLocations("file:///"+WebMvcConfig.FILE_DIR); } }

实体对象和dao层:

package com.zzg.dao; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.zzg.entity.Address; @Repository public interface AddressRepository extends JpaRepository<Address, Long> { } package com.zzg.dao; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.zzg.entity.People; @Repository public interface PeopleRepository extends JpaRepository<People, Long> { } package com.zzg.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; @Entity @Table(name = "address") @Data // 自动生成get、set、toString、equals方法 @AllArgsConstructor // 全参构造方法 @NoArgsConstructor // 无参构造方法 @Accessors(chain = true) // 链式编程 public class Address{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Long id;//id @Column(name = "phone", nullable = true, length = 11) private String phone;//手机 @Column(name = "zipcode", nullable = true, length = 6) private String zipcode;//邮政编码 @Column(name = "address", nullable = true, length = 100) private String address;//地址 } package com.zzg.entity; import java.util.Date; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; @Entity @Table(name = "people") @Data // 自动生成get、set、toString、equals方法 @AllArgsConstructor // 全参构造方法 @NoArgsConstructor // 无参构造方法 @Accessors(chain = true) // 链式编程 public class People{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Long id;//id @Column(name = "name", nullable = true, length = 20) private String name;//姓名 @Column(name = "sex", nullable = true, length = 1) private String sex;//性别 @Column(name = "birthday", nullable = true) private Date birthday;//出生日期 @OneToOne(cascade=CascadeType.ALL)//People是关系的维护端,当删除 people,会级联删除 address @JoinColumn(name = "address_id", referencedColumnName = "id")//people中的address_id字段参考address表中的id字段 private Address address;//地址 }

功能测试(test)定义

package com.zzg.test; import java.util.Date; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.alibaba.fastjson.JSONObject; import com.zzg.dao.AddressRepository; import com.zzg.dao.PeopleRepository; import com.zzg.entity.Address; import com.zzg.entity.People; /** * Spring-data-jpa 一对一测试 * @author zzg * */ @RunWith(SpringRunner.class) @SpringBootTest public class OneToOne { @Autowired private PeopleRepository peopleRepository; @Autowired private AddressRepository adressRepository; @Test public void insertOneToOne(){ Address address = new Address(); address.setAddress("广东省深圳市"); address.setPhone("13265778956"); address.setZipcode("430821"); People people = new People(); people.setName("测试"); people.setSex("1"); people.setBirthday(new Date()); people.setAddress(address); peopleRepository.save(people); } @Test public void selectOneToOne(){ People people = peopleRepository.findOne(1L); System.out.println(people.getName()); System.out.println(people.getAddress().toString()); } @Test public void updateOneToOne(){ Address address = adressRepository.findOne(1L); address.setAddress("地址修改"); People people = peopleRepository.findOne(1L); people.setAddress(address); peopleRepository.save(people); } @Test public void deleteOneToOne(){ peopleRepository.delete(1L); } }

增删改查效果截图:

修改:

新增: 

 查询

 删除:

 

最新回复(0)