Автор оригинала: Vlad Mihalcea.
Отвечая на вопросы на форуме Hibernate , я наткнулся на следующий вопрос об использовании аннотации @ManyToOne
, когда столбец внешнего ключа на стороне клиента ссылается на столбец непервичного ключа на родительской стороне.
В этой статье вы увидите, как использовать аннотацию @JoinColumn
для размещения ассоциаций “многие к одному”, не относящихся к первичному ключу.
Предполагая, что в нашей базе данных есть следующие таблицы:
Столбец isbn
в таблицах публикация
и книга
связаны с помощью ограничения внешнего ключа, которое является основой нашей @ManyToOne
ассоциации:
Книга
представляет родительскую сторону ассоциации, и она отображается следующим образом:
@Entity(name = "Book") @Table(name = "book") public class Book implements Serializable { @Id @GeneratedValue private Long id; private String title; private String author; @NaturalId private String isbn; //Getters and setters omitted for brevity }
Столбец isbn
отображается как @Natural
, поскольку его также можно использовать в качестве бизнес-ключа.
Для получения более подробной информации об аннотации @Natural
ознакомьтесь с этой статьей .
Публикация
представляет дочернюю структуру ассоциации, поэтому она будет отображена следующим образом:
@Entity(name = "Publication") @Table(name = "publication") public class Publication { @Id @GeneratedValue private Long id; private String publisher; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn( name = "isbn", referencedColumnName = "isbn" ) private Book book; @Column( name = "price_in_cents", nullable = false ) private Integer priceCents; private String currency; //Getters and setters omitted for brevity }
По умолчанию ассоциация @ManyToOne
предполагает, что идентификатор сущности на стороне родителя должен использоваться для соединения со столбцом внешнего ключа сущности на стороне клиента.
Однако при использовании связи с непервичным ключом referencedColumnName
следует использовать для указания Hibernate, какой столбец следует использовать на родительской стороне для установления связи базы данных “многие к одному”.
Предполагая, что в нашей базе данных есть следующие объекты:
Book book = new Book(); book.setTitle( "High-Performance Java Persistence" ); book.setAuthor( "Vlad Mihalcea" ); book.setIsbn( "978-9730228236" ); entityManager.persist(book); Publication amazonUs = new Publication(); amazonUs.setPublisher( "amazon.com" ); amazonUs.setBook( book ); amazonUs.setPriceCents( 4599 ); amazonUs.setCurrency( "$" ); entityManager.persist( amazonUs ); Publication amazonUk = new Publication(); amazonUk.setPublisher( "amazon.co.uk" ); amazonUk.setBook( book ); amazonUk.setPriceCents( 3545 ); amazonUk.setCurrency( "&" ); entityManager.persist( amazonUk );
После извлечения Публикации
вместе с связанной с ней Книгой
мы видим , что @ManyToOne
ассоциация работает должным образом:
Publication publication = entityManager.createQuery( "select p " + "from Publication p " + "join fetch p.book b " + "where " + " b.isbn = :isbn and " + " p.currency = :currency", Publication.class) .setParameter( "isbn", "978-9730228236" ) .setParameter( "currency", "&" ) .getSingleResult(); assertEquals( "amazon.co.uk", publication.getPublisher() ); assertEquals( "High-Performance Java Persistence", publication.getBook().getTitle() );
При выполнении приведенного выше запроса JPQL Hibernate генерирует следующую инструкцию SQL:
SELECT p.id AS id1_1_0_, b.id AS id1_0_1_, p.isbn AS isbn5_1_0_, p.currency AS currency2_1_0_, p.price_in_cents AS price_in3_1_0_, p.publisher AS publishe4_1_0_, b.author AS author2_0_1_, b.isbn AS isbn3_0_1_, b.title AS title4_0_1_ FROM publication p INNER JOIN book b ON p.isbn = b.isbn WHERE b.isbn = '978-9730228236' AND p.currency = '&'
Как вы можете видеть, referencedColumnName
позволяет настроить предложение JOIN
| ON таким образом, чтобы столбец
isbn использовался вместо идентификатора сущности по умолчанию.
Если вы хотите представить ассоциацию @ManyToOne
, не являющуюся первичным ключом, вам следует использовать атрибут referencedColumnName
аннотации @JoinColumn|/.
Для более сложных ситуаций, например, когда вам нужно использовать пользовательскую функцию SQL в предложении JOIN
| ON ,
вы можете использовать специальную аннотацию @JoinFormula для спящего режима .