You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JPA复合主键(外键+日期)设置日期自增及Java Play时间追踪类问题

嘿,我来帮你搞定这个Java Play项目里JPA复合主键日期字段自增的问题!首先得明确你说的“自增”大概率是两种场景:要么自动填充当前时间,要么在同一用户+项目组合下按特定规则递增日期。下面给你几个适配Play框架的实用方案:

方案1:自动填充当前时间(最常用场景)

如果你的需求是每次创建Tracking记录时,自动把主键里的日期字段设为当前系统时间,用JPA的生命周期注解@PrePersist就能轻松实现,完全不用依赖数据库特性。

首先假设你的TrackingPK嵌入式主键是这样的(补全你没写完的部分):

@Embeddable
public class TrackingPK implements Serializable {
    private Long user;    // 对应用户ID
    private Long project; // 对应项目ID
    private LocalDateTime date; // 要自动设置的日期字段

    // 必须实现的equals、hashCode方法,以及getter/setter
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TrackingPK that = (TrackingPK) o;
        return Objects.equals(user, that.user) &&
               Objects.equals(project, that.project) &&
               Objects.equals(date, that.date);
    }

    @Override
    public int hashCode() {
        return Objects.hash(user, project, date);
    }

    // getter/setter 省略
}

然后在你的Tracking实体类里添加@PrePersist方法,在持久化前自动填充日期:

@Entity(name = "trackings")
public class Tracking extends Model {
    @EmbeddedId
    public TrackingPK id;

    @ManyToOne
    @MapsId("user")
    @JoinColumn(name="user", referencedColumnName="id")
    public User user;

    @ManyToOne
    @MapsId("project")
    @JoinColumn(name="project", referencedColumnName="id")
    public Project project;

    @Required
    @Enumerated(EnumType.STRING)
    public ETrackingStatus status; // 补全你的枚举类型

    // 持久化前自动设置日期
    @PrePersist
    public void prePersist() {
        if (id == null) {
            id = new TrackingPK();
        }
        // 同步用户和项目ID到主键
        id.setUser(user.getId());
        id.setProject(project.getId());
        // 设置当前时间作为日期字段
        id.setDate(LocalDateTime.now());
    }

    // 其他业务方法省略
}

这样每次调用tracking.save()时,JPA会自动触发prePersist方法,帮你把日期字段设为当前时间,同时确保复合主键的完整性。

方案2:按自定义规则递增日期(同一用户&项目下)

如果你的需求是同一用户+项目组合下,日期按特定规则递增(比如每次比上一条记录晚1小时,或者按天递增且不重复),那需要先查询该组合的最新记录,再计算下一个日期。

在Tracking实体类里添加一个静态工厂方法来处理这个逻辑:

@Entity(name = "trackings")
public class Tracking extends Model {
    // 现有字段省略...

    // 静态方法:创建带递增日期的Tracking实例
    public static Tracking createWithIncrementedDate(User user, Project project) {
        Tracking tracking = new Tracking();
        tracking.user = user;
        tracking.project = project;

        // 查询该用户+项目的最新记录
        Tracking latestTracking = find.query()
                .where()
                .eq("user", user)
                .eq("project", project)
                .orderBy("id.date desc")
                .setMaxRows(1)
                .findOne();

        LocalDateTime nextDate;
        if (latestTracking == null) {
            // 没有历史记录,用当前时间作为初始值
            nextDate = LocalDateTime.now();
        } else {
            // 这里自定义递增规则:比如每次递增1小时
            nextDate = latestTracking.id.getDate().plusHours(1);
            
            // 如果是按天递增(确保当天无重复),可以替换成下面的逻辑:
            // LocalDateTime today = LocalDateTime.now().truncatedTo(ChronoUnit.DAYS);
            // if (latestTracking.id.getDate().truncatedTo(ChronoUnit.DAYS).isBefore(today)) {
            //     nextDate = today;
            // } else {
            //     nextDate = latestTracking.id.getDate().plusDays(1);
            // }
        }

        // 组装复合主键
        TrackingPK pk = new TrackingPK();
        pk.setUser(user.getId());
        pk.setProject(project.getId());
        pk.setDate(nextDate);
        tracking.id = pk;

        return tracking;
    }

    // 其他方法省略
}

使用的时候直接调用这个工厂方法:

// 获取当前用户和目标项目
User currentUser = ...;
Project targetProject = ...;

// 创建带递增日期的追踪记录
Tracking newTracking = Tracking.createWithIncrementedDate(currentUser, targetProject);
newTracking.status = ETrackingStatus.IN_PROGRESS; // 设置枚举状态
newTracking.save();

方案3:依赖数据库默认值

如果你更倾向于让数据库来处理日期的自动生成,可以给TrackingPK的日期字段设置数据库层面的默认值(比如MySQL的CURRENT_TIMESTAMP):

@Embeddable
public class TrackingPK implements Serializable {
    private Long user;
    private Long project;

    // 设置数据库列默认值为当前时间
    @Column(name = "tracking_date", columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    private LocalDateTime date;

    // equals、hashCode、getter/setter 省略
}

注意:这种方式下,JPA不会自动把数据库生成的日期同步到实体对象中,如果需要在save()后获取这个日期值,需要调用entityManager.refresh(tracking)来刷新实体(Play框架里可以通过JPA.em().refresh(tracking)实现)。

额外注意:复合主键的唯一性

不管用哪种方案,都要确保user+project+date的组合是唯一的,建议在Tracking实体类上添加唯一约束:

@Table(uniqueConstraints = {
    @UniqueConstraint(columnNames = {"user", "project", "tracking_date"})
})
@Entity(name = "trackings")
public class Tracking extends Model {
    // 现有字段省略...
}

内容的提问来源于stack exchange,提问作者Anthony PERIQUET

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:16:07