spring jpa 如何给外键赋值

本文深入探讨了使用SpringDataJPA时,如何正确处理实体间的一对一关系,特别是在外键字段赋值时遇到的问题。通过实例解析,揭示了@OneToOne注解下cascade属性设置不当可能导致的异常,并提供了解决方案。

最近在用spring data jap 的时候遇到一个问题,就是无法给一张表的外键赋予值,
在A实体类中有一个属性的是另一个实体类B
如图
在这里插入图片描述
@JoinColumn的name指的是数据库表中的外键字段uid在这里插入图片描述
这个外键在数据库中是vachar型的,但是在我的程序里却是一个实体类型的就是那个patient类
那么如果我们要在数据库表中添加一条数据,且还要为uid这个字段赋值的话,要怎么做呢?
如下图
在这里插入图片描述
这样就行了,这个问题困扰了我两三天,一开始也不是没想过用这种办法,而且实验了,但是失败了
失败的原因是因为@OneToOne注解被我设置了 cascade=CascadeType.PERSIST 这个属性,然后一运行就抛异常,不知道是什么原因 找了找网上的说法是 因为
"CascadeType.PERSIST只有A类新增时,会级联B对象新增。若B对象在数据库存(跟新)在则抛异常(让B变为持久态)"这个,但我也不是很理解这句话的意思。之所以写这篇博客是因为在网上找不到把这个问题说的很清楚的博客或者资料,希望其他新人能脱离这个坑。
以下是实体类chat

@Entity(name = "chat")
public class chat {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id")
	int id;
	@Lob
	@Basic(fetch = FetchType.LAZY)
	@Column(name = "msg", columnDefinition = "Text")
	String msg;
	@Lob
	@Basic(fetch = FetchType.LAZY)
	@Column(name = "hfmsg", columnDefinition = "Text")
	String hfmsg;
	@Column(name = "savetime")
	String savetime;
	@OneToOne
	@JoinColumn(name = "uid")
	patient patient;

	public int getId() {
		return id;
	}

	public patient getPatient() {
		return patient;
	}

	public void setPatient(patient patient) {
		this.patient = patient;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public String getHfmsg() {
		return hfmsg;
	}

	public void setHfmsg(String hfmsg) {
		this.hfmsg = hfmsg;
	}

	public String getSavetime() {
		return savetime;
	}

	public void setSavetime(String savetime) {
		this.savetime = savetime;
	}
}

另一个实体类patient

@Entity(name="patient")
public class patient {
		@Id
		@GeneratedValue(strategy=GenerationType.IDENTITY)
		@Column(name="id")
		int id;
		@Column(name="uname")
		String uname;
		@Column(name="upass")
		String upass;
		@Column(name="tname")
		String tname;
		@Column(name="sex")
		String sex;
		@Column(name="age")
		String age;
		@Column(name="idcard")
		String idcard;
		@Column(name="tel")
		String tel;
		@Column(name="addr")
		String addr;
		@Column(name="delstatus")
		String delstatus;
		public int getId() {
			return id;
		}
		public void setId(int id) {
			this.id = id;
		}
		public String getUname() {
			return uname;
		}
		public void setUname(String uname) {
			this.uname = uname;
		}
		public String getUpass() {
			return upass;
		}
		public void setUpass(String upass) {
			this.upass = upass;
		}
		public String getTname() {
			return tname;
		}
		public void setTname(String tname) {
			this.tname = tname;
		}
		public String getSex() {
			return sex;
		}
		public void setSex(String sex) {
			this.sex = sex;
		}
		public String getAge() {
			return age;
		}
		public void setAge(String age) {
			this.age = age;
		}
		public String getIdcard() {
			return idcard;
		}
		public void setIdcard(String idcard) {
			this.idcard = idcard;
		}
		public String getTel() {
			return tel;
		}
		public void setTel(String tel) {
			this.tel = tel;
		}
		public String getAddr() {
			return addr;
		}
		public void setAddr(String addr) {
			this.addr = addr;
		}
		public String getDelstatus() {
			return delstatus;
		}
		public void setDelstatus(String delstatus) {
			this.delstatus = delstatus;
		}
		
}

<think>好的,用户之前询问了如何在Spring Boot中使用JPA实现子表反查父表的数据展示,现在他要求不使用JPA。我需要考虑用户可能的背景和需求。用户可能对JPA不够熟悉,或者项目中有其他持久层框架的限制,比如使用MyBatis或者纯JDBC。也可能用户希望更直接地控制SQL查询,避免ORM框架带来的复杂性。 首先,我需要回顾之前的回答,确保新的方法不使用JPA,但保持功能相同。用户需要实体类、DTO、Repository、Service和Controller的示例,但这次不使用JPA的注解和接口。所以需要替换为基于JDBC或者MyBatis的实现方式。 考虑到用户可能更倾向于使用Spring的JdbcTemplate,因为它在Spring Boot中集成方便,无需复杂的配置。同时,使用JdbcTemplate可以展示更底层的SQL操作,适合用户希望直接控制查询的情况。 接下来,我需要设计实体类,去掉JPA的注解,改为普通的Java类。例如,Order和OrderItem类不再有@Entity、@Id等注解,而是简单的POJO。然后,Repository层需要手动处理数据库连接和查询,将结果集映射到实体对象。这里可以使用RowMapper或者BeanPropertyRowMapper来实现结果集到对象的映射。 在Service层,需要调用Repository的方法获取数据,并转换为DTO。Controller层保持不变,处理HTTP请求并返回响应。 需要注意的是,在查询子表时关联父表数据,需要编写JOIN查询的SQL语句,手动处理结果集中的父表字段,并将其设置到DTO中。例如,查询OrderItem时同时获取Order的相关字段,然后在RowMapper中将这些字段组合到DTO里。 另,要处理数据库连接和事务管理,确保在Repository中使用JdbcTemplate的方法时,正确管理资源。可能还需要配置数据源,但用户可能已经了解这部分,所以示例中可以省略配置细节,专注于核心逻辑。 测试数据插入部分,需要手动编写插入SQL,使用JdbcTemplate的update方法执行。响应示例和注意事项也需要调整,比如提到使用JOIN避免N+1问题手动映射字段,以及处理结果集的方法。 最后,需要对比之前的JPA实现,说明不同方法的优缺点,比如更灵活但需要更多手动编码,适合对SQL控制要求高的场景。确保用户理解两种方法的适用情况,以便根据项目需求选择合适的方式。</think>以下是基于Spring Boot **不使用JPA**(使用纯JDBC或MyBatis风格)实现子表反查父表的完整示例: --- ### 1. 实体类定义(无JPA注解) ```java // 父表 Order.java public class Order { private Long id; private String orderNumber; private LocalDateTime createTime; // getters/setters } // 子表 OrderItem.java public class OrderItem { private Long id; private String productName; private Integer quantity; private Long orderId; // 关联父表的 // getters/setters } ``` --- ### 2. DTO对象(带父表信息) ```java public class OrderItemWithOrderDTO { private Long itemId; private String productName; private Integer quantity; private String orderNumber; // 父表字段 private LocalDateTime orderTime; // 构造方法(通过SQL查询结果直接映射) public OrderItemWithOrderDTO(Long itemId, String productName, Integer quantity, String orderNumber, LocalDateTime orderTime) { // 字段赋值... } // getters } ``` --- ### 3. Repository实现(使用JdbcTemplate) ```java @Repository public class OrderItemRepository { private final JdbcTemplate jdbcTemplate; public OrderItemRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } // 带JOIN的查询方法 public List<OrderItemWithOrderDTO> findItemsWithOrderInfo(String productName) { String sql = """ SELECT i.id AS item_id, i.product_name, i.quantity, o.order_number, o.create_time AS order_time FROM order_items i INNER JOIN orders o ON i.order_id = o.id WHERE i.product_name = ? """; return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(OrderItemWithOrderDTO.class), productName); } } ``` --- ### 4. Service层 ```java @Service public class OrderService { private final OrderItemRepository orderItemRepository; public OrderService(OrderItemRepository orderItemRepository) { this.orderItemRepository = orderItemRepository; } public List<OrderItemWithOrderDTO> getItemsWithOrderInfo(String productName) { return orderItemRepository.findItemsWithOrderInfo(productName); } } ``` --- ### 5. 自定义RowMapper(复杂映射场景) ```java public class OrderItemMapper implements RowMapper<OrderItemWithOrderDTO> { @Override public OrderItemWithOrderDTO mapRow(ResultSet rs, int rowNum) throws SQLException { return new OrderItemWithOrderDTO( rs.getLong("item_id"), rs.getString("product_name"), rs.getInt("quantity"), rs.getString("order_number"), rs.getTimestamp("order_time").toLocalDateTime() ); } } // 在Repository中使用 public OrderItemWithOrderDTO findById(Long itemId) { String sql = "SELECT ... WHERE i.id = ?"; return jdbcTemplate.queryForObject(sql, new OrderItemMapper(), itemId); } ``` --- ### 6. 测试数据插入 ```java @SpringBootTest public class DataInitializer { @Autowired private JdbcTemplate jdbcTemplate; @Test void insertTestData() { jdbcTemplate.update("INSERT INTO orders(order_number, create_time) VALUES (?, ?)", "20230816-001", LocalDateTime.now()); Long orderId = jdbcTemplate.queryForObject("SELECT LAST_INSERT_ID()", Long.class); jdbcTemplate.update(""" INSERT INTO order_items(product_name, quantity, order_id) VALUES (?, ?, ?) """, "笔记本电脑", 1, orderId); } } ``` --- ### 响应示例(与JPA版本一致) ```json { "itemId": 1, "productName": "笔记本电脑", "quantity": 1, "orderNumber": "20230816-001", "orderTime": "2023-08-16T09:15:22" } ``` --- ### 关点说明: 1. **SQL显式控制**:通过手写JOIN语句实现关联查询 2. **结果集映射**: - 简单场景使用`BeanPropertyRowMapper`(要求数据库字段名与Java属性名一致) - 复杂场景自定义`RowMapper` 3. **DTO直接构造**:通过SQL别名(如`AS order_time`)匹配构造函数参数 4. **性能优化**:单次SQL查询获取所有数据,避免N+1问题 --- ### 对比JPA方案的差异: | 特性 | JPA方案 | 非JPA方案 | |--------------------|--------------------------|-----------------------| | 关联关系管理 | 自动对象导航 | 手动JOIN查询 | | SQL控制 | 生成或JPQL | 完全手写 | | 结果映射 | 自动ORM映射 | 手动RowMapper/DTO构造 | | 事务管理 | 声明式事务 | 需要手动控制 | | 适合场景 | 快速开发简单CRUD | 需要复杂SQL优化的场景 | --- 如果需要更底层的实现(如纯JDBC),可以进一步封装`Connection`和`PreparedStatement`,但推荐使用`JdbcTemplate`保持代码简洁性。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值