Удивительно, но я дошел до этого момента в жизни, не имея отношений между родителем и ребенком. Я не буду останавливаться на том, как много примеров я посмотрел, кроме как сказать, что я смотрел на MANY . Этот парень кажется одним из лучших. Единственная проблема в том, что каждый пример, который я могу найти, является либо «сокращенным» (только те части, которые, по мнению автора, были интересны, либо настолько старые, что они вообще не компилируются, либо не используют JPA и т. Д. И т. Д.
Мой очень простой случай - у меня есть объект, представляющий конечную точку отдыха. В дочерней таблице будет строка для каждого http-заголовка, необходимого для вызова. (Конечно, это еще не все, но это самый простой пример).
Целевая среда - это текущие версии всего, например. Пружинный ботинок 2.1.3. Релиз и т. Д.
После долгих раздумий я создал строки БД, хотя "Я" думаю, что должен быть лучший способ. Однако я не могу найти находку для создания экземпляра дочерней коллекции.
МОДЕЛЬ РОДИТЕЛЯ (некоторые столбцы удалены только для того, чтобы сделать сообщение короче)
@Entity
@Table(name = "EXT_PROVIDER")
public class ExtProvider extends AuditModel {
private String serviceName;
private String serviceType;
@Id
@Column(name = "SERVICE_NAME", nullable = false, length = 30)
public String getServiceName() {
return serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
@Basic
@Column(name = "SERVICE_TYPE", nullable = false, length = 50)
public String getServiceType() {
return serviceType;
}
public void setServiceType(String serviceType) {
this.serviceType = serviceType;
}
@OneToMany(mappedBy = "extProvider", cascade = CascadeType.ALL, orphanRemoval = true)
public List<ExtProviderHeader> headers = new ArrayList<>();
public void addExtProviderHeader(ExtProviderHeader header) {
headers.add(header);
header.setExtProvider(this);
}
public void removeExtProviderHeader(ExtProviderHeader header) {
headers.remove(header);
header.setExtProvider(null);
}
public void setExtProviderHeader(List<ExtProviderHeader> headers) {
this.headers = headers;
}
public void setHeaders(List<ExtProviderHeader> headers) {
this.headers = headers;
}
Если это некомментированный, спящий режим перестает работать с
hibernate.MappingException: не удалось определить тип для: java.util.List, для таблицы: ext_provider, для столбцов: [org.hibernate.mapping.Column (headers)]
Но если вы не можете сделать это, как @Service должен получать данные, даже если они загружаются
//public List<ExtProviderHeader> getHeaders() {
// return headers;
//}
МОДЕЛЬ РЕБЕНКА
@Entity
@Table(name = "EXT_PROVIDER_HEADER")
public class ExtProviderHeader {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "extProviderSeq")
@SequenceGenerator(name = "extProviderSeq", sequenceName = "EXT_PROVIDER_SEQ", allocationSize = 5)
@Column(name = "DATA_ID", nullable = false, precision = 0)
private Long dataId;
public Long getDataId() {
return dataId;
}
public void setDataId(Long dataId) {
this.dataId = dataId;
}
@Basic
@Column(name = "HEADER_KEY", nullable = false, length = 30)
private String headerKey;
public String getHeaderKey() {
return headerKey;
}
public void setHeaderKey(String headerKey) {
this.headerKey = headerKey;
}
@Basic
@Column(name = "HEADER_VALUE", nullable = false, length = 50)
private String headerValue;
public String getHeaderValue() {
return headerValue;
}
public void setHeaderValue(String headerValue) {
this.headerValue = headerValue;
}
@Basic
@Column(name = "SERVICE_NAME", nullable = false, length = 30, insertable = true, updatable = true)
private String serviceName;
public String getServiceName() { return serviceName; }
public void setServiceName(String serviceName) { this.serviceName = serviceName; }
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "SERVICE_NAME", insertable = false, updatable = false)
private ExtProvider extProvider;
public ExtProvider getExtProvider() { return extProvider; }
public void setExtProvider(ExtProvider extProvider) { this.extProvider = extProvider; }
репозитории
@Repository
public interface ExtProviderRepository extends JpaRepository<ExtProvider, String> {
}
@Repository
public interface ExtProviderHeaderRepository extends JpaRepository<ExtProviderHeader, Long> {
ExtProviderHeader findByDataId(Long primaryKey);
}
JUNIT для создания строк
Я думаю, что нет никакого способа обойти множественные сохранения, потому что первичный ключ для дочерних строк - это SEQUENCE, и существует отношение FOREIGN KEY к Parent. Так что, хотя мне это не нравится, кажется, застряло.
Приветствуются разные советы
@Test
@DisplayName("Save Provder and Headers")
public void saveProvider() {
ExtProvider extProvider = new ExtProvider();
extProvider.setServiceName("MS6");
extProvider.setServiceType("MESSAGING");
extProvider = extProviderRepository.save(extProvider);
ExtProviderHeader extProviderHeader = new ExtProviderHeader();
extProviderHeader.setHeaderKey("Authorization: OAuth");
extProviderHeader.setHeaderValue("xxxx-xxxxxxxx");
extProviderHeader.setServiceName(extProvider.getServiceName());
extProviderHeader = extProviderHeaderRepository.save(extProviderHeader);
extProvider.addExtProviderHeader(extProviderHeader);
extProviderHeader = new ExtProviderHeader();
extProviderHeader.setHeaderKey("Content-Type");
extProviderHeader.setHeaderValue("application/json");
extProviderHeader.setServiceName(extProvider.getServiceName());
extProviderHeader = extProviderHeaderRepository.save(extProviderHeader);
extProvider.addExtProviderHeader(extProviderHeader);
ExtProvider save = extProviderRepository.save(extProvider);
logger.info("Provider Ceated");
}
JUNIT для создания экземпляра объекта обратно в память
Здесь коллекция headers возвращается пустой.
@Test
public void createProvider() {
Optional<ExtProvider> optional = extProviderRepository.findById("MS6");
ExtProvider extProvider = optional.get();
logger.info("PROVIDER:{}", extProvider);
List<ExtProviderHeader> headers = extProvider.headers;
for(ExtProviderHeader header : headers) {
logger.info("Header is:{}", header);
}
Замечания:
- Очевидно, что отсутствие получателя заголовков нарушает ОО. Но если я раскомментирую, то getter hibernate не сможет инициализироваться. Я здесь упускаю важный момент.
- В некоторых примерах говорится, что LIST заменяется на SET или даже COLLECTION. Это не имеет никакого значения.
- В большинстве примеров НЕ имеется Модель, которая фактически создает экземпляр коллекции. Хотя ссылка, которую я указал в верхней части поста, есть. Этот парень является основным участником проекта hibernate, поэтому я хочу доверять ему. Это было бы проще, если бы его код работал!
- Независимо от того, если Модель не выделяет ArrayList, тогда JUNIT получает NPE, когда он пытается перебрать коллекцию. Конечно, все это действительно говорит нам о том, что Hibernate даже не пытался установить эту коллекцию. Я ожидаю, что Hibernate установит пустой список, если это вообще сработает.
РЕДАКТИРОВАТЬ: Я обнаружил, что я могу получить заголовки, сделав этот вызов:
extProvider.setHeaders(extProviderHeaderRepository.findByExtProvider(extProvider));
Это немного круто, наверное. Я ожидал, что, когда я "сделаю ссылку" на спящий сборник, он будет заполнен - вот как я читаю FetchType.LAZY должен работать. Конечно, переход на FetchType.Eager ничего другого не делал.
Я также выяснил, если я не использую «get» в названии метода, т.е. изменим getHeaders на headersList Hibernate прекратит попытки сопоставить коллекцию с базой данных колонка.
Так что со всем этим он работает более или менее. Но это все полностью вручную. Я не вижу реального преимущества в том, чтобы иметь их как отдельные объекты и вручную управлять ими.
Может быть, я все еще что-то упускаю. ИЛИ может быть, это всего лишь одна абстракция, слишком далеко, чтобы действительно работать так, как рекламируется. Я буду продолжать тыкать в это.