JPA, смешанный суррогатный ключ с внешним ключом и порядковым номером - PullRequest
0 голосов
/ 30 ноября 2010

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

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 имеет сгенерированный Id для DOC_ID, в то время как Section имеет составной первичный ключ над DOC_ID и SECTION_NUM.

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

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

@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);

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

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

Ответы [ 2 ]

0 голосов
/ 21 июня 2013

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

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

@MapsId("docId")

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

0 голосов
/ 29 декабря 2010

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

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

Ранее я упоминал, что класс Document выглядит следующим образом.

@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 легко обработал бы отношение, поскольку он принял бы идентификатор из приложения или сгенерировал бы его из последовательности, но он не будет делать оба с одним идентификатором.

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

@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);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...