0

i have a problem with @oneToMany relation between 2 class: My first class is

@Data
@Entity
@Table(name = "job")
public class Jobs {

    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer jobId;

    @OneToMany
    private List<JobsStatus> jobStatus;
}

and my JobsStatus class

@Data
@Entity
@Table(name = "job_status")
public class JobsStatus {

    @Id
    @Column(columnDefinition = "VARCHAR(20)")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private String jobStatus;

    @Column(name="sysdate")
    private Date sysDate;
}

JobsStatus is a static table in database, this is the result when i select on the table

COMPLETED null

ERROR null

INSERTED null

PROCESSING null

VALIDATED null

VALIDATING null

and this is ok, because the date should be null

after i run my code i can see the relation table in my database (job_job_status) and i get this result

irjobs_job_id job_status_job_status

1 INSERTED

1 PROCESSING

1 VALIDATED

1 VALIDATING

This is ok, but, every time i update my job class, i need to see sysDate on job_job_status table like this:

jobs_job_id job_status_job_status sysDate

1 INSERTED 04/14/2023

1 PROCESSING 04/14/2023

1 VALIDATED 04/14/2023

1 VALIDATING 04/14/2023

simply i need one more column, sysDate, on the relation table (job_job_status )

i try to update like this:

JobsStatus jobStatus = new IRJobsStatus();
jobStatus.setJobStatus("INSERTED");
jobStatus.setSysDate(new java.sql.Date(new java.util.Date().getTime()));
jobStatusList.add(jobStatus);

can someone help me?

Marco MM
  • 1
  • 3
  • Your data model does not make sense. If the timestamps are supposed to be tied to specific jobs, then why do you have a column for them on `JobStatus` at all? – John Bollinger Apr 14 '23 at 14:05
  • 1
    Also, if multiple jobs must each have independent collections of the same set of statuses, then that's many-to-many, not one-to-many. And in that case, you're probably looking for [JPA 2.0 many-to-many with extra column](https://stackoverflow.com/q/23837561). – John Bollinger Apr 14 '23 at 14:10
  • i need oneToMany, one job can have multiple status and i need to see the date in relationTable, not in job table or, as you say, in jobStatus table – Marco MM Apr 14 '23 at 14:14
  • So if one job has status `COMPLETED`, then no other job can have that status? That is implied by the "one" part of "one-to-many" in your scheme. Likewise, at most one with `ERROR`, at most one with `VALIDATED`, *etc*. – John Bollinger Apr 14 '23 at 14:22
  • correct, when the jobs is finished it should not be modified anymore, whatever the state is – Marco MM Apr 14 '23 at 14:23
  • You say "correct", but what you say afterward does not seem to match what you are agreeing with. Maybe it would help if you edit the question to explain the application logic that uses these entities. – John Bollinger Apr 14 '23 at 14:27
  • What exactly is going wrong when you try to update? – ryanwebjackson Apr 15 '23 at 12:17

1 Answers1

0

JPA models persistent state via entities. You cannot tag relationships with their own state. If you find yourself wanting to do so then that's a sign that you need to interpose an additional entity.

Typical procedures for that are described among the answers to JPA 2.0 many-to-many with extra column. The same approach can be used for a one-to-many in which the relationship is modeled via a join table, but if the relationship is one-to-many then the data can ordinarily be recorded in the entities on the "many" side instead, since each of those is specific to / owned by exactly one entity on the "one" side anyway.

As to the the specifics of your question:

JobsStatus is a static table in database

I take you to mean that this table is never modified. It serves only to enumerate the allowed status codes such as "COMPLETED". (A more common variation also associates those codes with surrogate primary keys, but that's not essential.)

But if it is never modified, then it is not useful for this table to have the sysDate column that is null for all rows. This carries no information, and never will carry any information because these rows are not supposed to be modified. Moreover, if the database must support dates for the same status for different jobs at the same time, then this column is not sufficient anyway.

i try to update like this:

JobsStatus jobStatus = new IRJobsStatus();
jobStatus.setJobStatus("INSERTED");
jobStatus.setSysDate(new java.sql.Date(new java.util.Date().getTime()));
jobStatusList.add(jobStatus);

If you set an entity property such as via jobStatus.setSysDate() then you should expect any persistent representation to be stored in the entity's table. Moreover, if you create a new entity (i.e. a JobStatus) and persist it, then you should expect a new row in that entity's table. That would not be appropriate for my understanding of a "static" table.

Overall, I think this is exactly the situation described in the linked question, but you're coming at it from a different angle. The things with which your Jobs have a one-to-many relationship are not statuses, but status dates. And, I presume, there may be many status dates (for different jobs) with for the same status. Thus, the multiplicities are:

Job (1) --- (many) JobStatusDate (many) --- (1) JobStatus

If you collapse that to look at the Job:JobStatus multiplicity, it is many-to-many.

I hope the direction I'm heading is already clear. You need to introduce a new entity to carry the dates of each job's various statuses. That can be structured multiple ways, but the one that most closely matches your database design would be something like this:

@Entity
public class Job {
    @Id
    private Integer jobId;

    @OneToMany(mappedBy = job)
    private List<JobStatusDate> statusDates;
}

@Entity
public class JobStatusDate {

    @Id
    @ManyToOne
    private Job job;

    @Id
    @ManyToOne
    private JobStatus status;

    private Date date;
}

@Entity
public class JobStatus {

    @Id
    private String status;

    // optional:
    @OneToMany(mappedBy = status)
    private List<JobStatusDate> statusDates;
}

Alternatively, if the application needs built-in knowledge of the valid job status codes (likely, since I anticipate that they are associated with specific business logic) then you could consider dropping the JobStatus entity (but keeping the table and the FK relationship in the database). Then JobStatusDate would have a plain String status code instead of a relationship with a JobStatus entity. The FK relationship retained in the DB would prevent invalid status codes from being recorded, which I guess is its intended purpose already.

In that event, you might consider going even further by mapping the status codes to an appropriate Java enum instead of to a String. The ultimate result might go as far as this:

public enum JobStatus {
    COMPLETED, ERROR, INSERTED, PROCESSING, VALIDATED, VALIDATING
}

@Entity
public class Job {
    @Id
    private Integer jobId;

    @OneToMany(mappedBy = job)
    @MapKey(name = status)
    private Map<JobStatus, JobStatusDate> statusDates;
}

@Entity
public class JobStatusDate {

    @Id
    @ManyToOne
    private Job job;

    @Id
    @Enumerated(EnumType.STRING)
    private JobStatus status;

    private Date date;
}

// No JobStatus entity

Notably, this allows you to map the Job : JobStatusDate relationship as a map, which would be a convenience for many of the kinds of things I anticipate you might want to do.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157