使用GenerationType.IDENTITY时PostgreSQL实体ID始终为null问题
我之前在处理Java EE + Open Liberty + PostgreSQL的组合时,也碰到过完全一样的问题——数据库里能看到主键生成了,但实体里的id就是null。结合你的场景,大概率是下面几个环节没处理到位,你可以逐一排查:
1. 事务上下文缺失是最常见的原因
你的addTodo方法有没有被事务包裹?JPA的persist()方法只是把实体加入持久化上下文,只有当事务提交或者手动flush的时候,才会真正执行数据库插入操作,并将生成的主键赋值回实体。
如果你的方法没有添加@Transactional注解,Open Liberty不会自动开启事务,这时候persist之后根本没和数据库交互,id自然是null。解决方法很简单:
- 在方法或者类上加上
javax.transaction.Transactional注解:import javax.transaction.Transactional; @Transactional public Long addTodo(final Todo todo) { LOG.info("Add todo"); entityManager.persist(todo); // 要是想立刻拿到id,也可以手动flush,不过事务提交时会自动做 // entityManager.flush(); LOG.info("Created todo: {}", todo); return todo.getId(); }
2. 数据库字段类型和实体类型不匹配
你数据库里用的是SERIAL类型,这在PostgreSQL里对应的是int4(Java的Integer类型),但你的实体类里id是Long类型。虽然PostgreSQL会自动做类型转换,但JPA的provider(Open Liberty默认用EclipseLink)可能没法正确把返回的int值映射到Long字段上,导致id无法被赋值。
解决方法二选一:
- 把数据库的
col_id改成BIGSERIAL(对应Java的Long):CREATE TABLE IF NOT EXISTS PUBLIC.tab_todo ( col_id BIGSERIAL PRIMARY KEY, col_name VARCHAR(80) NOT NULL, col_desc VARCHAR(500), col_due TIMESTAMP NOT NULL, col_state BOOLEAN NOT NULL ); - 或者把实体类的id字段改成
Integer:@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "col_id", nullable = false, updatable = false) private Integer id;
3. 明确配置EclipseLink的目标数据库
Open Liberty默认使用EclipseLink作为JPA provider,如果你没在persistence.xml里指定目标数据库,EclipseLink可能无法正确识别PostgreSQL的IDENTITY主键生成策略。
在你的persistence.xml里添加以下配置:
<persistence-unit name="YourPersistenceUnitName" transaction-type="JTA"> <jta-data-source>jdbc/YourDataSourceName</jta-data-source> <class>your.package.Todo</class> <properties> <!-- 明确指定目标数据库为PostgreSQL --> <property name="eclipselink.target-database" value="PostgreSQL"/> <!-- 可选:开启日志查看JPA执行细节,方便排查问题 --> <property name="eclipselink.logging.level" value="FINE"/> </properties> </persistence-unit>
4. 确认PostgreSQL驱动版本兼容性
确保你用的PostgreSQL驱动版本和PostgreSQL 12兼容,建议使用42.2.x及以上的稳定版本。如果是用Maven管理依赖,pom.xml里的配置应该是:
<dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.2.24</version> <!-- 如果Open Liberty已经内置了驱动,就加上scope=provided,否则去掉 --> <scope>provided</scope> </dependency>
按照上面的步骤排查后,应该就能解决persist后id为null的问题了。
内容的提问来源于stack exchange,提问作者Apollo

