Спящий режим / постоянство без @Id
У меня есть представление базы данных, которое дает набор результатов, который не имеет истинного первичного ключа. Я хочу использовать Hibernate/Persistence, чтобы отобразить этот набор результатов на объекты Java. Конечно, потому что нет PK, я не могу украсить любое поле @Id
,
При развертывании Hibernate жалуется на пропавших без вести @Id
, Как я могу обойти это?
8 ответов
Если есть комбинация столбцов, которая делает строку уникальной, смоделируйте класс первичного ключа вокруг комбинации столбцов. Если нет, то вам в основном не повезло, но вам следует пересмотреть дизайн представления, так как это, вероятно, не имеет смысла.
Есть пара разных подходов:
@Entity
public class RegionalArticle implements Serializable {
@Id
public RegionalArticlePk getPk() { ... }
}
@Embeddable
public class RegionalArticlePk implements Serializable { ... }
Или же:
@Entity
public class RegionalArticle implements Serializable {
@EmbeddedId
public RegionalArticlePk getPk() { ... }
}
public class RegionalArticlePk implements Serializable { ... }
Подробности здесь: http://docs.jboss.org/ejb3/app-server/HibernateAnnotations/reference/en/html_single/index.html
Вот сообщение, описывающее похожую проблему: http://www.theserverside.com/discussions/thread.tss?thread_id=22638
Вместо поиска обходных путей в Hibernate может быть проще добавить фиктивный идентификатор в представление базы данных. Давайте предположим, что у нас есть представление PostgreSQL с двумя столбцами, и ни один из них не является уникальным (и нет первичного ключа, так как Postgres не позволяет устанавливать PK или любые другие ограничения для представлений) ex.
| employee_id | project_name |
|:------------|:-------------|
| 1 | Stack01 |
| 1 | Jira01 |
| 1 | Github01 |
| 2 | Stack01 |
| 2 | Jira01 |
| 3 | Jira01 |
------------------------------
Который представлен следующим запросом:
CREATE OR REPLACE VIEW someschema.vw_emp_proj_his AS
SELECT DISTINCT e.employee_id,
pinf.project_name
FROM someschema.project_info pinf
JOIN someschema.project_employee pe ON pe.proj_id = pinf.proj_id
JOIN someschema.employees e ON e.employee_id = pe.emloyee_id
Мы можем добавить фиктивный идентификатор, используя row_number():
SELECT row_number() OVER (ORDER BY subquery.employee_id) AS row_id
как в этом примере:
CREATE OR REPLACE VIEW someschema.vw_emp_proj_his AS
SELECT row_number() OVER (ORDER BY subquery.employee_id) AS row_id,
subquery.employee_id,
subquery.project_name
FROM
(SELECT DISTINCT e.employee_id,
pinf.project_name
FROM someschema.project_info pinf
JOIN someschema.project_employee pe ON pe.proj_id = pinf.proj_id
JOIN someschema.employees e ON e.employee_id = pe.emloyee_id ) subquery;
И таблица будет выглядеть так:
| row_id | employee_id | project_name |
|:------------|:------------|:-------------|
| 1 | 1 | Stack01 |
| 2 | 1 | Jira01 |
| 3 | 1 | Github01 |
| 4 | 2 | Stack01 |
| 5 | 2 | Jira01 |
| 6 | 3 | Jira01 |
-------------------------------------------
Теперь мы можем использовать row_id как @Id в JPA/Hibernate/Spring Data:
@Id
@Column(name = "row_id")
private Integer id;
Как в примере:
@Entity
@Table(schema = "someschema", name = "vw_emp_proj_his")
public class EmployeeProjectHistory {
@Id
@Column(name = "row_id")
private Integer id;
@Column(name = "employee_id")
private Integer employeeId;
@Column(name = "project_name")
private String projectName;
//Getters, setters etc.
}
Для каждого объекта вы должны указать хотя бы одно из следующего:
- один @Id
- множественный @Id и @IdClass (для составного первичного ключа)
- @EmbeddedId
так, может быть, вы можете создать составной первичный ключ, содержащий несколько полей?
Вот пример, который имеет 2 ключа в качестве ключей "Id": https://gist.github.com/3796379
Измените ваш выбор, используемый для создания представления:
SELECT
ROWNUM ID, -- or use nextval of some sequence
-- other columns
FROM
TABLE
и сопоставьте "ID" как первичный ключ.
Вы можете просто использовать @EmbeddedId следующим образом:
@EmbeddedId
public Id getId() {
return id;
}
public void setId(Id id) {
this.id = id;
}
Вы можете проверить, есть ли логический идентификатор и предоставить соответствующую информацию. Hibernate не будет проверять базу данных на наличие определенного первичного ключа.
Согласно руководству пользователя Hibernate, рекомендуется использовать pk, поэтому даже если вы работаете с существующим проектом, который не содержит pk на стороне базы данных, вы можете добавить столбец pk в соответствующую таблицу базы данных и сопоставить его с Java сущности.
Рекомендация спящего режима была такой:
Мы рекомендуем вам объявлять атрибуты идентификатора с одинаковыми именами в постоянных классах и использовать тип оболочки (т. Е. Непримитивный) (например, Long или Integer).
Более подробную информацию о механизме pk и Hibernate можно найти здесь.
Хотя это не совсем то, что вы просите, вот небольшой прием, которым я пользуюсь. Попросите запрос выбрать "rownum" и определить "rownum" в качестве столбца идентификатора в модели. Это эффективно сделает каждый ряд уникальным для Hibernate.