JPA, смешанный суррогатный ключ с внешним ключом и порядковым номером

StackOverflow https://stackoverflow.com/questions/4309472

Вопрос

У меня есть две таблицы:

DOCUMENT
--------
DOC_ID (PK)
.
.
.

SECTION
-------
DOC_ID (FK, PK)
SECTION_NUM (PK)
.
.
.

Записи в базе данных могут выглядеть так:

Документ:

DOC_ID | . . .
--------------
1      | . . .
2      | . . .

Раздел:

DOC_ID | SECTION_NUM | . . .
---------------------------
1      | 1           | . . .
1      | 2           | . . .
1      | 3           | . . .
2      | 1           | . . .

Document имеет сгенерированный идентификатор на doc_id, пока Section Имеет композитный первичный ключ над doc_id и section_num.

Раздел_num - это локально (приложение), создаваемое порядковым числом, начинающим свежим для каждого документа.

Мои классы сущности выглядят следующим образом:

@Entity
@Table(name = "DOCUMENT")
public class Document implements java.io.Serializable {
    @Id
    @Column(name = "DOC_ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "DocIdSeq")
    @SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1)
    private Long docId;
}


@Entity
@Table(name = "SECTION")
@IdClass(SectionId.class)
public class Section implements java.io.Serializable {
    @Id
    @Column(name = "DOC_ID", nullable = false)
    private Long docId;

    @Id
    @Column(name = "SECTION_NUM", nullable = false)
    private Integer sectionNum;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "DOC_ID")
    private Document document;
}

public class SectionId implements java.io.Serializable {
    private Long docId;
    private Integer sectionNum;
}

При вставке нового документа и соответствующего раздела я делаю следующее:

Document doc = new Document();

Section section = new Section();
section.setDocument(doc);
section.setSectionNum(1);

entityManager.persist(doc);

При сохранении я получаю исключение, указав, что NULL не допускается для раздела столбца_num. Я использую OpenEJB (который опирается на OpenJPA за сценами для тестирования подразделения), и нашел при наступлении через код OpenJPA, который он успешно сохраняет объект документа, но когда речь идет о объекте разделе, он создает новый экземпляр и устанавливает все Поля в NULL, поэтому теряя ценность в разрезе, прежде чем связывать его на объект документа, сохранялся ранее.

К сожалению, я не могу изменить схему БД, как это устаревшая система. Кто-нибудь сделал что-то похожее и получил его работать?

Это было полезно?

Решение

Я имел в виду, чтобы обновить это в течение некоторого времени, но был слишком занят ...

Хорошо, так оказывается, что это не возможно с JPA. Тем не менее, есть обходной путь.

Ранее я упомянул, что класс документа выглядит так.

@Entity
@Table(name = "DOCUMENT")
public class Document implements java.io.Serializable {
    @Id
    @Column(name = "DOC_ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
    "DocIdSeq")
    @SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1)
    private Long docId;
}

Это была только укороченная версия, чтобы уточнить вопрос. Настоящий класс также имеет коллекцию разделов:

@Entity
@Table(name = "DOCUMENT")
public class Document implements java.io.Serializable {
    @Id
    @Column(name = "DOC_ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
"DocIdSeq")
    @SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1)
    private Long docId;

    @OneToMany
    private Set<Section> sections = new HashSet<Section>(0);
}

Если бы раздел был простым первичным ключом, JPA будет легко справиться с отношениями, так как он примет ID из приложения или генерирует его из последовательности, но она не будет делать оба с одним идентификатором.

Итак, решение состоит в том, чтобы управлять отношениями самостоятельно и добавить функцию жизненного цикла:

@Entity
@Table(name = "DOCUMENT")
public class Document implements java.io.Serializable {
    @Id
    @Column(name = "DOC_ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
    "DocIdSeq")
    @SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1)
    private Long docId;

    @Transient
    private Set<Section> sections = new HashSet<Section>(0);

    @PostPersist
    public void updateChildIds() {
        for (Section section : this.sections) {
            section.getId().setDocId(this.docId);
        }
    }
}

Как видите, соотношение раздела теперь переходно, то есть JPA не будет управлять этим. После того, как сохраняют документ, структура позвонит функцию UpdateChildIds, вручную обновляю идентификатор раздела с новым сохраненным идентификатором документа.

Это может быть продемонстрировано в следующем фасаде:

@Stateless
public void DocumentFacade implements DocumentFacadeLocal {

    @PersistenceContext
    private EntityManager entityManager;

    public void save(Document entity) throws Exception {
        this.entityManager.persist(entity);
        this.entityManager.flush();
        this.persistTransientEntities(entity);
        this.entityManager.flush();
    }

    private void persistTransientEntities(CaseInstructionSheet entity) {
       for (Section section : entity.getSections()) {
            this.entityManager.persist(section);
        }
    }
}

Другие советы

На самом деле, JPA идеально может справиться с этим. Аннотация, которую вы ищете, это MapsId.

В вашем случае, в вашем Section, на docId Вам просто нужно добавить следующее:

@MapsId("docId")

Значение MapsId Аннотация - это имя атрибута вашего составного первичного ключа (который в этом случае одинаково)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top