2016-11-21 3 views
2

У меня есть родительская таблица i.e. audit_log (parent), которая содержит один идентификатор столбца. Для данного идентификатора в audit_log у меня есть список идентификаторов поставщиков. Я храню их в отдельной таблице audit_log_vendorid (дочерняя таблица). Я хочу, чтобы дочерняя таблица получала идентификатор из родительской таблицы как один из столбцов (parent_id). Вот схема таблицы.Отображение родительского ребенка Hibernate JPA

audit_log

+-------+------------+------+-----+---------+-------+ 
| Field | Type  | Null | Key | Default | Extra | 
+-------+------------+------+-----+---------+-------+ 
| id | bigint(19) | NO | PRI | NULL |  | 
+-------+------------+------+-----+---------+-------+ 

audit_log_vendorid

+-----------+------------+------+-----+---------+----------------+ 
| Field  | Type  | Null | Key | Default | Extra   | 
+-----------+------------+------+-----+---------+----------------+ 
| id  | bigint(19) | NO | PRI | NULL | auto_increment | 
| vendor_id | bigint(19) | NO |  | NULL |    | 
| parent_id | bigint(19) | NO |  | NULL |    | 
+-----------+------------+------+-----+---------+----------------+ 

I'have определило мои классы Hibernate следующим

@Entity 
@Table(name="audit_log") 
public class AuditLog { 

private List<AuditVendorPair> vendorIDs; 


public AuditLog(List<AuditVendorPair> vendorIds) throws Exception { 
    this.vendorIDs = vendorIDs; 
} 

@OneToMany(cascade=CascadeType.ALL) 
@JoinTable(name = "audit_log_vendorid", 
    joinColumns = { @JoinColumn(name = "parent_id", referencedColumnName="id") }) 
public List<AuditVendorPair> getVendors() { 
    return vendorIDs; 
} 

@Id @Column(name="ID") 
public Long getId() { 
    return super.getId(); 
} 

public void setHostServices(List<AuditVendorPair> vendorIDs){ 
    this.vendorIDs = vendorIDs; 
} 

} 

Мой гибернации класс отображения для audit_log_vendorid ниже. Я передаю идентификатор поставщика и ожидаю, что два других поля будут заполнены спящим режимом. Поле parent_id, которое я хочу в поле «id» в audit_log. Он инициализируется как null, поскольку теперь вызывает исключение ограничений mysql.

@Entity 
@Table(name="audit_log_vendorid") 
public class AuditVendorPair { 

private Long id; 
private Long parent_id; 
private Long vendor_id; 
public AuditVendorPair(Long vendor_id){ 
    this.vendor_id = vendor_id; 
} 

@Id 
@GeneratedValue(strategy = GenerationType.IDENTITY) 
@Column(name="id") 
public Long getId(){ 
    return id; 
} 

public void setId(Long id){ 
    this.id = id; 
} 

@Column(name="vendor_id") 
public Long getVendorID() { 
    return vendor_id; 
} 

public void setVendorID(Long vendor_id){ 
    this.vendor_id = vendor_id; 
} 


@Column(name="parent_id") 
public Long getParentId() { 
    return parent_id; 
} 

public void setParentId(Long parentID){ 
    this.parent_id = parentID; 
} 
} 

Мне любопытно узнать, соответствуют ли мои аннотации. В основном я хочу, чтобы идентификатор из таблицы audit_log был заполнен в поле parent_id в таблице audit_log_vendorid с помощью hibernate.

ответ

1

Нет, они не верны. audit_log_vendorid не является таблицей объединения. Таблица соединений - это таблица, которая не привязана к сущности и содержит идентификаторы двух связанных объектов, сопоставленных с другими таблицами.

У вас также не должно быть поля parent_id в AuditVendorPair. Не только потому, что это не соответствует соглашениям об именах Java, но также и потому, что его следует заменить ссылкой на AuditLog, сопоставленной с ManyToOne.

Итак, вкратце, вы должны иметь двунаправленную ассоциацию OneToMany, отображаемую, как описано в the documentation.

+0

Да audit_log_vendorid не является таблицей соединений. В чем причина обращения к AuditLog, сопоставленная с ManyToOne? Я думал, что однонаправленный OneToMany будет работать. Используя внешний ключ, указываем ли мы в спящий режим, что ему нужно извлечь значение для parent_id из столбца с ссылкой? Меняются вещи, если мой первичный ключ в таблице audit_log больше идентификатора? – skeptic01

+0

Вы можете использовать однонаправленный OneToMany, но тогда столбец parent_id в audit_log_vendorid будет обрабатываться ассоциацией OneToMany (вам понадобится '@JoinColumn (" parent_id ")) и не должен отображаться второй раз в объекте AuditVendorPair. Если вам нужно получить доступ к родительскому объекту из объекта AuditVendorPair, тогда он должен быть двунаправленным. –

1

Я думаю, что вы игнорируете ключевую концепцию в JPA, которая является объектами, поэтому у вас никогда не будет объекта, ссылающегося на его родительский элемент, с использованием идентификатора напрямую, вы должны ссылаться на объект (JPA будет использовать идентификатор, когда запрашивая DB)

@Entity 
@Table(name="audit_log") 
public class AuditLog { 

    @OneToMany(cascade= CascadeType.ALL, mappedBy = "auditLog") 
    private Collection<AuditVendorPair> vendorIDs; 

    @Id @Column(name="id") 
    private Long id; 

    public AuditLog() { 
    } 

    public Collection<AuditVendorPair> getVendors() { 
     if (vendorIDs == null) { 
      vendorIDs = new ArrayList<>(); 
     } 
     return vendorIDs; 
    } 

    public long getId() { 
     return id; 
    } 
} 

и

@Entity 
@Table(name = "audit_log_vendorid") 
public class AuditVendorPair { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "id") 
    private Long id; 

    @JoinColumn(nullable = false, name = "parent_id") 
    @ManyToOne(optional = false) 
    private AuditLog auditLog; 

    @Column(name = "vendor_id") 
    private Long vendorId; 

    public AuditVendorPair() { 
    } 

    public long getVendorId() { 
     return vendorId; 
    } 

    public void setVendorId(long vendorId) { 
     this.vendorId = vendorId; 
    } 

    public AuditLog getAuditLog() { 
     return auditLog; 
    } 

    public void setAuditLog(AuditLog auditLog) { 
     this.auditLog = auditLog; 
    } 
} 

AuditVendorPair имеет в виду Auditlog с использованием Entity, и поскольку это отношение сущностей, вы должны использовать @JoinColumn указать имя.

Некоторые лучшие практики для JPA/Hibernate

  • Нет ARGS конструктора требуется по спецификации.
  • У вас нет сеттера для первичного ключа, если вы не сгенерируете его в коде.
  • Вы не можете использовать примитивы для полей. Однако, если база данных принудительно использует NOT NULL, вы должны использовать примитив в long get и seters, поэтому ваша среда IDE может предупредить вас о NPE, а не ждать, пока ваши тесты не сработают.
  • Использование коллекции не является списком, так как список подразумевает порядок.
  • Done использовать символы подчеркивания в именах полей, но использовать их при указании имени столбца.
  • Обратите внимание на создание нулевой проверки и создания коллекции в getVendors(), чтобы избежать NPE при первом создании объекта. Это означает, что вы можете использовать ту же логику для добавления AuditVendorPair, независимо от того, только что был создан AuditLog или загружен из БД. Это также означает, что я не создаю setVendors(), если мне не нужно заменить весь список, что редко бывает, и когда это так, вам обычно нужно явно удалять все сущности в списке.
+0

Спасибо за информацию. Я получаю следующее, когда я пытаюсь и сохраняю AuditLog: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Столбец 'audit_id' не может быть нулевым Нужно ли добавлять ссылку на внешний ключ из родительского родительского родительского столбца? – skeptic01

+0

См. Ответ ниже для примера кода. –

0

Похоже, вы должны прочитать немного больше о принципах, стоящих за JPA. Вот пример «сырой» JPA того, как будет выглядеть код, если вы используете Spring-data и autowire @PersistanceUnit, вам не нужно управлять самим entityManager.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("PU-Name"); 
EntityManager em = emf.createEntityManager(); 

try { 
    long primaryKey = 1L; // comes from somewhere else 
    long vendorId = 1L; // comes from somewhere 
    AuditLog log = em.find(AuditLog.class, primaryKey); // loaded from DB 

    AuditVendorPair pair = new AuditVendorPair(); 
    pair.setAuditLog(log); 
    pair.setVendorId(vendorId); 
    em.getTransaction().begin(); 
    em.persist(pair); 
    em.getTransaction().commit(); 
} finally { 
    em.close(); 
} 

Вы должны подключить сущности в коде, и любые существующие объекты должны быть загружены из базы данных. Если вы храните новый AuditVendorPair, сначала нужно загрузить объект AuditLog, а затем установить его на вновь созданном AuditVendorPair. Обычно Vendor также является Entity, поэтому его также нужно искать.

Примечание: Приведенный выше пример не поддерживает двусторонние отношения между AuditLog и AuditVendorPair, поскольку AuditVendorPair не добавляется к коллекциям поставщиков в AuditLog. Поскольку столбец, определяющий отношение, находится в AuditVendorPair (который хранится), это не проблема, так как в следующий раз, когда вы загружаете экземпляр AuditLog из БД, AuditVendorPair будет частью коллекции поставщика. Однако, если вы используете AuditLog после закрытия контекста сохранения, вам может потребоваться поддерживать двустороннюю связь.

Примечание: пример выше предполагает, что вы получаете запросы с первичными ключами. В общем, вы никогда не должны открывать первичные ключи в любом интерфейсе, в наших системах мы генерируем уникальное поле (UUID) для каждого объекта, который отображается в пользовательском интерфейсе.