У меня есть эта сущность:
public class StatementLinesEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long statementLinesId;
@CreationTimestamp
@Temporal(TemporalType.DATE)
private Date dateOperation;
private String operationNature;
private BigDecimal amount;
private String debitAmount;
И эта сущность имеет Наследование типа SINGLE_TABLE:
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class OperationCreditEntity {
@Id
@Column(nullable = false, updatable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private Long operationCreditId;
@CreatedDate
private Date operationDate;
@OneToOne
private StatementLinesEntity statementLine;
И эти 3 объекта наследуют его:
@Entity
@DiscriminatorValue("Espece")
public class OperationEspecesEntity extends OperationCreditEntity {
private String cin;
private String nomEmetteur;
private String prenomEmetteur;
=============================
@DiscriminatorValue("Virement")
public class OperationVirementEntity extends OperationCreditEntity {
private String rib;
===========================
@Entity
@DiscriminatorValue("Cheque")
public class OperationChequeEntity extends OperationCreditEntity{
private int numeroCheque;
Предположим, у меня есть List<StatementLinesEntity>
, состоящий из 2 строк, в строке есть debitAmount = C
и operationNature = Virement
, а во второй строке есть debitAmount = C
и operationNature = Espece
. Моя цель — сохранить каждую строку в определенном DTYPE
. пример
первая строка должна сохраняться в OperationCreditEntity table DTYPE = Virement
, а вторая должна сохраняться в OperationCreditEntity table DTYPE = Espece
Да, почему я не создаю экземпляры, потому что я создаю службу, которая будет отвечать только за эти операции, все транзакции будут перенесены в таблицу OperationCreditEntity
, этот процесс будет выполняться автоматически, это как вы даете 100000 строк, и эти службы будут помещать каждая строка к определенному DTYPE на основе этих двух условий: должна иметь C
в debitAmount
и может содержать одно из этих 3 слов: Virement or Espece or Cheque
в operationNature
.
Как я уже сказал, это служба, отвечающая за этот процесс, дело в том, что я не нашел логики, как достичь этой цели. На самом деле я застрял в этом вопросе около 2 дней без решений. Если вы что-то знаете, пожалуйста, скажите
До сих пор не понятно - что у вас сейчас конкретно? Является ли это миграцией существующих данных таблицы StatementLinesEntity в другой формат таблицы, или вы берете пользовательские данные и задаетесь вопросом, как превратить ваши существующие данные StatementLinesEntity в эти квази-объекты данных OperationCreditEntity java; Цикл for, использующий ваш экземпляр StatementLinesEntity для создания из него экземпляра OperationCreditEntity с вызовом spring repo.save() для результата
Если вы контролируете сами входящие данные (например, JSON), вы можете просто встроить их непосредственно в StatementLinesEntity, который ссылается на OperationCreditEntity, синтаксические анализаторы могут построить его для вас с небольшими изменениями, на которые я намекал, чтобы вам не нужно было дублировать данные dateOperation и operationNature в обе сущности/таблицы
Хорошо, рассмотрим этот сценарий. У меня есть таблица StatementLinesEntity
в mysql с 100000 записей. В моей таблице будут все поля с данными (id, date, bigDecimal amount, C, Virement), где C
относится к debitAmount
, а Virement
относится к operationNature
, в моей базе данных у меня есть еще 4 таблицы OperationCreditEntity
с аннотацией @Inheritance типа SINGLE_TABLE и 3 другие таблицы, которые наследуются от нее (OperationVirementEntity,OperationChequeEntity,OperationEspeceEntity), пока все ясно.
Теперь в моем сервисе я пойду в базу данных и пройдусь по всем строкам StatementLinesEntity
, а затем отфильтрую каждую строку, в которой есть debitAmount == C
и operationNature == Virement or Cheque or Espece
(это могут быть другие данные, которые не имеют этих условий, я буду их игнорировать) после фильтрации и возврата нового Список (список, в котором есть все debitAmount == C
, я хочу сохранить каждую запись в OperationCreditEntity
в определенном DTYPE на основе operationNature
, например: первая запись в списке имеет debitAmount == C
и operationNature == Virement
мой сервис сохраняет эту строку как DTYPE = Virement
в таблице OperationCreditEntity
. теперь вторая запись имеет debitAmount == C
и operationNature == Cheque
, служба должна сохранить эту строку как DTYPE = Cheque
в таблице OperationCreditEntity
. третья запись имеет debitAmount == C
и operationNature == Espece
, служба должна сохранять эту строку как DTYPE = Espece
и так далее ... и каждая строка, сохраненная в таблице OperationCreditEntity
, будет иметь FK StatementLinesEntity
, потому что у меня есть @OneToOne с StatementLinesEntity
в OperationCreditEntity
. Я не знаю, если вы получите идею.
Вы смешиваете «сущность» или класс данных Java с таблицей. Насколько я понимаю, у вас всего 2 таблицы. Один для StatementLinesEntity, а другой для данных OperationCreditEntity. То, что вы помещаете в эти таблицы, кажется мне, немного перекрывается, и, кажется, существует отношение 1: 1. Таким образом, если у вас есть 100 тыс. строк StatementLinesEntity, у вас, вероятно, будет 100 тыс. строк OperationCreditEntity, дублирующих данные OperationNature в обоих. Независимо от того, где вы застряли? Прочтите/примите свой StatementLinesEntity, создайте из него экземпляры OperationCreditEntity и вызовите save.
Вы имеете в виду, что я должен перебрать список и создать экземпляр OperationCreditEntity, а затем вызвать метод сохранения для OperationCreditEntity?
Есть ли способ, которым я могу установить дискриминатор программно, то есть я зацикливаю каждую запись, в которой я устанавливаю дискриминатор на свой собственный DTYPE. Есть ли решение для этого?
Я думаю, вы запутались в наследовании Java. Смотрите мой ответ, но поймите, что new OperationChequeEntity() является экземпляром OperationCreditEntity. JPA обрабатывает запись DTYPE для вас на основе DiscriminatorValue в подклассах. Вам никогда не нужно создавать «новый OperationCreditEntity()», поскольку он не имеет значения для вашего приложения — поэтому я пометил его как абстрактный в своем ответе. Если вы выполняете запрос к таблице OperationCreditEntity, никогда не должно быть строк, которые сопоставляются с OperationCreditEntity — для нее нет возможной записи DTYPE.
Модель для меня должна быть больше похожа на:
@Entity
public class StatementLinesEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long statementLinesId;
@CreationTimestamp
@Temporal(TemporalType.DATE)
private Date dateOperation;
@OneToOne(mappedBy = "statementLine")
private OperationCreditEntity operation;
private BigDecimal amount;
private String debitAmount;
}
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
abstract public class OperationCreditEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long operationCreditId;
@CreatedDate
private Date operationDate;
@OneToOne
private StatementLinesEntity statementLine;
}
Затем любой метод, который принимает экземпляры StatementLinesEntity, может затем принимать тот, который ссылается на экземпляр OperationCreditEntity (который может быть любым из его подклассов). Нет необходимости управлять, анализировать или обрабатывать строки String operationNature напрямую, так как тип операции будет определять характер операции.
Это может изменить другие подписи, сериализацию (например, JSON), поэтому, если вы не можете использовать это и «застряли» с существующим представлением данных StatementLinesEntity, ВАМ необходимо справиться с тем, как создавать экземпляры OperationCreditEntity из этих данных. Нет инструмента, который автоматически сделает это за вас. Это так же просто, как утилита вида:
OperationCreditEntity createOperationInstance(StatementLinesEntity statementLine) {
String operationNature = statementLine.getOperationNature();
OperationCreditEntity returnVal = null;
if "Espece".equals(operationNature) {
returnVal = new OperationEspecesEntity();
} else if "Virement".equals(operationNature) {
returnVal = new OperationVirementEntity();
} else if "Cheque".equals(operationNature) {
returnVal = new OperationChequeEntity();
} else {
throw new IllegalStateException();
}
returnVal.setStatementLine(statementLine);
return returnVal;
}
Просто вызовите save, используя репозиторий OperationCreditEntity, когда бы вы ни вызывали этот метод, чтобы поместить его в тот же транзакционный контекст, в который вы вносите изменения. Также обратите внимание, что в этих подклассах OperationCreditEntity есть данные, которые вам нужно найти, чтобы заполнить их самостоятельно; Я лично думаю, что эти данные, вероятно, будут привязаны к данным, доступным при определении/создании StatementLinesEntity, поэтому их следует генерировать/создавать тогда, а не постфактум, но это зависит от вас.
Добавлено для полноты: Да, вы можете получить доступ к столбцу, используемому для хранения значений дискриминатора, непосредственно в базовом классе сущностей. Ничто не мешает и не мешает вам отображать столбец, как любой другой столбец базы данных. Для Hibernate используется «DTYPE», поэтому
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class OperationCreditEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long operationCreditId;
@CreatedDate
private Date operationDate;
@Column(name = "DTYPE",insertable=false, updatable=false)
private String typeValue;
}
Заметьте, я пометил это как вставляемый/обновляемый=ложный. Это зависит от поставщика, если он жалуется на управление этим значением таким образом; многие пытаются сделать это в надежде изменить это. Изменение типа объекта не поддерживается. Caterpillar не станет бабочкой, просто изменив строковое значение. Любые кэши, которые содержат OperationCreditEntity или какой-либо конкретный тип подкласса, не волшебным образом изменят тип объекта; JPA требует, чтобы вы удалили объект и создали новый экземпляр (соответствующего класса) для этих данных, желательно после сброса операции удаления.
Также обратите внимание, что вы можете запрашивать и использовать выражения типа сущности (TYPE) без столбца или другого сопоставления для него.
"Select line from OperationCreditEntity operation join operation.statementLine line where TYPE(operation) IN (OperationEspecesEntity, OperationChequeEntity) and line.somethingElse = :someValue"
Я не понимаю вопроса - вы упоминаете строки БД, но у вас есть классы и сущности Java. Разве вы не создаете экземпляры OperationEspecesEntity, OperationVirementEntity и OperationChequeEntity для своих данных? Зачем дублировать столбцы из StatementLinesEntity в отдельную таблицу OperationCreditEntity (при условии, что dateOperation~operationDate, DTYPE~operationNature). Один должен просто ссылаться на другой (нормализация БД) — StatementLinesEntity должен иметь ссылку на OperationCreditEntity вместо того, чтобы напрямую хранить операцию OperationNature.