Spring Boot中CommandLineRunner内Hibernate执行save操作报错的问题咨询
我来帮你分析下这个问题的根源,以及对应的解决办法:
问题原因分析
你遇到的报错Can not set int field springboottest.entity.Item.id to springboottest.entity.Item看起来有点反常——怎么会把Item对象赋值给int类型的id字段?主要是两个因素叠加导致的:
1. 实体类中的重复注解问题
你的Item实体类里重复添加了@GeneratedValue注解:
@Id @GeneratedValue // 多余的重复注解 @GeneratedValue(strategy = GenerationType.IDENTITY)
在普通Java环境下,Hibernate可能会忽略重复注解,默认取最后一个生效的配置;但在Spring Boot环境中,Spring对JPA注解的解析逻辑更严格,重复注解会导致Hibernate元数据解析异常,进而在生成并赋值id时出现类型错误。
2. Spring Boot自动配置与手动Hibernate上下文冲突
如果你的项目引入了spring-boot-starter-data-jpa依赖,Spring Boot会自动配置JPA相关的Bean(比如EntityManagerFactory、EntityManager),并且会对实体类进行代理增强。而你手动通过Configuration.buildSessionFactory()创建了一个独立的Hibernate SessionFactory,这两个独立的Hibernate上下文会产生冲突,导致手动创建的SessionFactory在处理被Spring代理后的实体类时,反射赋值出现异常。
解决办法
你可以根据自己的需求选择以下两种方案:
方案一:使用Spring管理的JPA/Hibernate组件(推荐)
既然已经用了Spring Boot,最好遵循Spring的生态,使用它管理的JPA组件,而不是手动创建SessionFactory:
步骤1:修正实体类注解
移除重复的@GeneratedValue注解,保留正确的配置:
@Entity @Table(name = "items") @NoArgsConstructor public class Item { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column @Getter @Setter private int id; @Getter @Setter @Column(nullable = false) private String name; public Item(String name) { this.name = name; } }
步骤2:注入EntityManager进行操作
修改DBInit类,注入Spring管理的EntityManager,再获取原生Session进行操作:
@Service public class DBInit implements CommandLineRunner { @PersistenceContext private EntityManager entityManager; @Override public void run(String[] args) { Session session = entityManager.unwrap(Session.class); Item item1 = new Item("itemName"); session.beginTransaction(); session.save(item1); session.getTransaction().commit(); } }
更简洁的方式:使用Spring Data JPA Repository
如果不想直接操作Session,还可以定义一个Repository接口,Spring会自动实现它:
// 定义Repository接口 public interface ItemRepository extends JpaRepository<Item, Integer> { } // 修改DBInit类注入Repository @Service public class DBInit implements CommandLineRunner { private final ItemRepository itemRepository; // 构造注入(Spring 4.3+支持无需@Autowired) public DBInit(ItemRepository itemRepository) { this.itemRepository = itemRepository; } @Override public void run(String[] args) { Item item1 = new Item("itemName"); itemRepository.save(item1); } }
方案二:禁用Spring Boot的JPA自动配置(坚持原生Hibernate)
如果你一定要用手动创建的SessionFactory,可以禁用Spring Boot的JPA自动配置:
步骤1:修正实体类注解
同样先移除重复的@GeneratedValue注解(和方案一的步骤1一致)。
步骤2:在启动类添加禁用配置
修改Spring Boot启动类,排除JPA自动配置类:
@SpringBootApplication(exclude = JpaAutoConfiguration.class) public class SpringBootTestApplication { public static void main(String[] args) { SpringApplication.run(SpringBootTestApplication.class, args); } }
这样你的手动SessionFactory代码就能和普通Java环境一样正常运行了。
为什么普通main方法没问题?
在普通Java环境中,没有Spring的自动配置和实体代理增强,Hibernate可以宽松地处理重复注解,也不存在上下文冲突的问题,所以代码能正常执行。但Spring Boot的环境带来了额外的自动配置和增强逻辑,放大了实体类注解的问题,最终导致报错。
内容的提问来源于stack exchange,提问作者Denys_newbie

