У меня есть следующий объект Parameter, который имеет самоассоциацию, я пытаюсь выполнить модульный тест, чтобы проверить свою реализацию, но мне кажется, что я делаю некоторые ошибки в своем отображении, и я не могу определить, где я ошибаюсь.
Моя сущность, как показано ниже
import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import com.tuto.common.enums.ParameterCategory;
import com.tuto.common.enums.ParameterType;
import com.tuto.common.enums.converter.ParameterTypePersistenceConverter;
/**
* This class represents the PARAMETERS SQL table as a java entity.
*
*/
@Entity
@Table(name = "PARAMETERS")
public class Parameter implements Serializable {
/**
* serialVersionUID.
*/
private static final long serialVersionUID = -732987999122243011L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SQ_PARAMETER_ID")
@SequenceGenerator(name = "SQ_PARAMETER_ID", sequenceName = "SQ_PARAMETER_ID", allocationSize = 1)
@Column(name = "PARAMETER_ID", unique = true)
private long id;
@Column(name = "PARAMETER_NAME")
private String name;
@Column(name = "PARAMETER_LABEL")
private String label;
@Column(name = "PARAMETER_COMMENT")
private String comment;
@JoinColumn(name = "ES_ID")
@OneToOne(fetch = FetchType.LAZY)
private ExpertSystem expertSystem;
@Column(name = "PARAMETER_CATEGORY")
@Enumerated(EnumType.ORDINAL)
private ParameterCategory category;
@Column(name = "PARAMETER_TYPE")
@Convert(converter = ParameterTypePersistenceConverter.class)
private ParameterType type;
@Column(name = "PARAMETER_CREATION_DATE",columnDefinition = "TIMESTAMP")
private OffsetDateTime creationDate;
@JoinColumn(name = "PARAMETER_CREATION_USER",referencedColumnName = "USER_FIRSTNAME")
@OneToOne(fetch = FetchType.LAZY)
private User creationUser;
@Column(name = "PARAMETER_UPDATE_DATE",columnDefinition = "TIMESTAMP")
private OffsetDateTime updateDate;
@JoinColumn(name = "PARAMETER_UPDATE_USER",referencedColumnName = "USER_FIRSTNAME")
@OneToOne(fetch = FetchType.LAZY)
private User updateUser;
@Column(name = "MULTIVALUE_SIZE")
private int multivalueSize;
@OneToMany(fetch = FetchType.LAZY)
@JoinTable(name = "dependences",
joinColumns = {@JoinColumn(name = "PARAMETER_ID")},
inverseJoinColumns = {@JoinColumn(name = "DEPENDANCE_ID")})
private List<Parameter> dependences = new ArrayList<>();
@OneToMany(fetch = FetchType.LAZY)
@JoinTable(name = "dependences",
joinColumns = {@JoinColumn(name = "DEPENDANCE_ID")},
inverseJoinColumns = {@JoinColumn(name = "PARAMETER_ID")})
private List<Parameter> consequences = new ArrayList<>();
public Parameter() {}
public Parameter(long id, String name, String label, String comment, ExpertSystem expertSystem,
ParameterCategory category, ParameterType parameterType, OffsetDateTime creationDate, User creationUser,
OffsetDateTime updateDate, User updateUser, int multivalueSize,List<Parameter> dependences) {
super();
this.id = id;
this.name = name;
this.label = label;
this.comment = comment;
this.expertSystem = expertSystem;
this.category = category;
this.type = parameterType;
this.creationDate = creationDate;
this.creationUser = creationUser;
this.updateDate = updateDate;
this.updateUser = updateUser;
this.multivalueSize = multivalueSize;
this.dependences = dependences;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public ExpertSystem getExpertSystem() {
return expertSystem;
}
public void setExpertSystem(ExpertSystem expertSystem) {
this.expertSystem = expertSystem;
}
public ParameterCategory getCategory() {
return category;
}
public void setCategory(ParameterCategory category) {
this.category = category;
}
public ParameterType getType() {
return type;
}
public void setType(ParameterType parameterType) {
this.type = parameterType;
}
public OffsetDateTime getCreationDate() {
return creationDate;
}
public List<Parameter> getDependences() {
return dependences;
}
public List<Parameter> getConsequences() {
return consequences;
}
public void setCreationDate(OffsetDateTime creationDate) {
this.creationDate = creationDate;
}
public User getCreationUser() {
return creationUser;
}
public void setCreationUser(User creationUser) {
this.creationUser = creationUser;
}
public OffsetDateTime getUpdateDate() {
return updateDate;
}
public void setUpdateDate(OffsetDateTime updateDate) {
this.updateDate = updateDate;
}
public User getUpdateUser() {
return updateUser;
}
public void setUpdateUser(User updateUser) {
this.updateUser = updateUser;
}
public int getMultivalueSize() {
return multivalueSize;
}
public void setMultivalueSize(int multivalueSize) {
this.multivalueSize = multivalueSize;
}
public void setDependences(List<Parameter> dependences) {
this.dependences = dependences;
}
public void setConsequences(List<Parameter> consequences) {
this.consequences = consequences;
}
public void addDependence(Parameter dependence) {
if (dependence != null ) {
removeConsequence(dependence);
this.dependences.add(dependence);
dependence.getConsequences().add(this);
}
}
public void removeDependence(Parameter dependence) {
if (dependence != null && this.dependences.contains(dependence) ) {
final int indexOfConsequence = this.dependences.indexOf(dependence);
Parameter parameter = this.dependences.get(indexOfConsequence) ;
parameter.getDependences().remove(this);
}
}
public void addConsequence(Parameter consequence) {
if (consequence != null ) {
removeConsequence(consequence);
this.consequences.add(consequence);
consequence.getDependences().add(this);
}
}
public void removeConsequence(Parameter consequence) {
if (consequence != null && this.consequences.contains(consequence) ) {
final int indexOfConsequence = this.consequences.indexOf(consequence);
Parameter parameter = this.consequences.get(indexOfConsequence) ;
parameter.getDependences().remove(this);
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (id ^ (id >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Parameter other = (Parameter) obj;
if (id != other.id)
return false;
return true;
}
@Override
public String toString() {
return "Parameter [id = " + id + ", name = " + name + ", label = " + label + ", comment = " + comment
+ ", expertSystem = " + expertSystem + ", category = " + category + ", type = " + type
+ ", creationDate = " + creationDate + ", creationUser = " + creationUser + ", updateDate = " + updateDate
+ ", updateUser = " + updateUser + ", multivalueSize = " + multivalueSize + ", dependences = " + dependences + "]";
}
}
Мой репозиторий, как показано ниже
import java.util.List;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.tuto.entity.Parameter;
/**
* This interface defines the contract of the possible operations to manage the parameter entity in the database.
*
*/
@Repository
public interface IParameterRepository extends JpaRepository<Parameter, Long> {
/**
* This method fetches the list of all available parameters. It could return an empty list.
*
* @return List<Parameter> The list of all Parameters found
*/
@Transactional
@Modifying(clearAutomatically = false)
@Query(value = "SELECT param FROM Parameter param")
List<Parameter> getAllParameters();
/**
* This method fetches the dependencies list (as Parameter list) for the given Param id.
* It could return an empty list.
*
* @param idParam The id of Parameter.
* @return List<Parameter> The dependencies list (as Parameter list) found for the given Param id.
*/
@Transactional
@Modifying(clearAutomatically = false)
@EntityGraph(attributePaths = {"dependences","expertSystem"})
@Query(value = "SELECT param.dependences FROM Parameter param join fetch param.dependences where param.id = :#{#idParam} ", nativeQuery = false)
List<Parameter> getDependancesByParamId(final long idParam);
/**
* This method fetches the consequences list (as Parameter list) for the given Param id.
* It could return an empty list.
*
* @param idParam The id of Parameter.
* @return List<Parameter> The dependencies list (as Parameter list) found for the given Param id.
*/
@Transactional
@Modifying(clearAutomatically = false)
@EntityGraph(attributePaths = {"consequences","expertSystem"})
@Query(value = "SELECT param.consequences FROM Parameter param join fetch param.consequences where param.id = :#{#idParam} ", nativeQuery = false)
List<Parameter> getConsequencesByParamId(final long idParam);
}
Мой модульный тест, как показано ниже
import static org.junit.Assert.assertEquals;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.ActiveProfiles;
import com.tuto.common.enums.ParameterCategory;
import com.tuto.common.enums.ParameterType;
import com.tuto.entity.Parameter;
import com.tuto.core.profile.PfSpringProfiles;
import com.tuto.test.Tags;
@DataJpaTest(showSql = true)
@Tag(Tags.COMPONENT)
@ActiveProfiles(PfSpringProfiles.TEST)
public class ParameterRepositoryTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private IParameterRepository parameterRepository;
@BeforeEach
public void intTests() {
final Parameter parameterInput = new Parameter();
parameterInput.setCategory(ParameterCategory.INPUT);
parameterInput.setType(ParameterType.DECIMAL);
parameterInput.setId(1L);
final Parameter parameterIntermediate = new Parameter();
parameterIntermediate.setCategory(ParameterCategory.INTERMEDIATE);
parameterIntermediate.setType(ParameterType.DECIMAL);
parameterIntermediate.setId(2L);
final Parameter parameterOutput = new Parameter();
parameterOutput.setCategory(ParameterCategory.OUTPUT);
parameterOutput.setType(ParameterType.DECIMAL);
parameterOutput.setId(3L);
entityManager.merge(parameterInput);
entityManager.merge(parameterIntermediate);
entityManager.merge(parameterOutput);
// parameterInput.getConsequences().add(parameterIntermediate);
parameterInput.addConsequence(parameterIntermediate);
parameterIntermediate.addConsequence(parameterOutput);
// parameterIntermediate.getConsequences().add(parameterOutput);
// parameterOutput.getDependences().add(parameterIntermediate);
// parameterIntermediate.getDependences().add(parameterInput);
entityManager.merge(parameterOutput);
entityManager.merge(parameterIntermediate);
entityManager.merge(parameterInput);
}
@Test
public void checkConsequencesAndDependencesAreNotEmpty() {
List<Parameter> parameter = parameterRepository.getDependancesByParamId(2L);
assertEquals(parameter.size(),1);
assertEquals(parameter.get(0).getId(),1L);
}
}
Я сталкиваюсь с двумя исключениями: первое — знаменитый сохранять отсоединенную сущность, второе — запрос указал выборку соединения, но владелец выбранной ассоциации не присутствовал в списке выбора.
Полная трассировка
Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=com.tuto.entity.Parameter.consequences,tableName=parameters,tableAlias=parameter2_,origin=parameters parameter0_,columns = {parameter0_.parameter_id,className=com.tuto.entity.Parameter}}] [SELECT param.consequences FROM com.tuto.entity.Parameter param join fetch param.consequences where param.id = :__$synthetic$__1 ]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:138)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:757)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:114)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:362)
at com.sun.proxy.$Proxy140.createQuery(Unknown Source)
at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:90)
... 88 common frames omitted
Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=com.tuto.entity.Parameter.consequences,tableName=parameters,tableAlias=parameter2_,origin=parameters parameter0_,columns = {parameter0_.parameter_id,className=com.tuto.entity.Parameter}}] [SELECT param.consequences FROM com.tuto.entity.Parameter param join fetch param.consequences where param.id = :__$synthetic$__1 ]
at org.hibernate.QueryException.generateQueryException(QueryException.java:120)
at org.hibernate.QueryException.wrapWithQueryString(QueryException.java:103)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:220)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:162)
at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:636)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:748)
... 96 common frames omitted
Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=com.tuto.entity.Parameter.consequences,tableName=parameters,tableAlias=parameter2_,origin=parameters parameter0_,columns = {parameter0_.parameter_id,className=com.tuto.entity.Parameter}}]
at org.hibernate.hql.internal.ast.tree.SelectClause.initializeExplicitSelectClause(SelectClause.java:215)
at org.hibernate.hql.internal.ast.HqlSqlWalker.useSelectClause(HqlSqlWalker.java:1028)
at org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:796)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:694)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:330)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:278)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:276)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:192)
... 102 common frames omitted
ва алейком салам, к сожалению, я не могу поделиться репозиторием, так как он размещен на локальном сервере, отделенном от внешнего мира
хорошо, я постараюсь создать аналогичный случай в моем регионе и обсудить с вами, решит ли это проблему.




Я исправил проблему, но я все еще сталкиваюсь с некоторыми вопросами, как показано ниже:
Исправление
@Transactional
@Modifying(clearAutomatically = false)
// @EntityGraph(attributePaths = {"dependences","expertSystem"})
@Query(value = "SELECT param.dependences FROM Parameter param where param.id = :#{#idParam} ", nativeQuery = false)
List<Parameter> getDependancesByParamId(final long idParam);
/**
* This method fetches the consequences list (as Parameter list) for the given Param id.
* It could return an empty list.
*
* @param idParam The id of Parameter.
* @return List<Parameter> The dependances list (as Parameter list) found for the given Param id .
*/
@Transactional
@Modifying(clearAutomatically = false)
// @EntityGraph(attributePaths = {"consequences","expertSystem"})
@Query(value = "SELECT param.consequences FROM Parameter param where param.id = :#{#idParam} ", nativeQuery = false)
List<Parameter> getConsequencesByParamId(final long idParam);
Я считаю, что, возможно, выбор элемента сущности param.consequences поможет мне избежать извлечения этой коллекции, однако что касается каждого элемента сущности ExpertSystem, здесь используется механизм ленивой загрузки, и я не смогу получить соединение с ним из коллекции. Кроме того, граф сущностей не должен помогать мне избежать этой проблемы путем автоматического извлечения коллекции или дочерних элементов !!!
Вы можете воспроизвести этот эксперимент на основе этого связь.
Если я правильно понял, то вы хотите понять, почему у вас не работает аннотация @EntityGraph. Насколько мне известно, аннотация @EntityGraph загружает узлы родительского объекта. Он не используется для загрузки родственного узла. Поэтому, если вы запрашиваете объект, у которого есть несколько элементов коллекции, таких как @OneToMany, @ManyToMany или @ElementCollection, вы можете с готовностью получить их все с аннотацией @EntityGraph. В вашем случае, чтобы получить expertSystem и consequences, ваш метод должен возвращать Parameter в качестве родительского объекта вместо List<Parameter>.
Обратите внимание, что в таком случае, если вы получите List, у вас есть хорошие шансы получить MultipleBagFetchException. Чтобы избежать этого, вы должны заменить List на Set.
@SyedMainulHasan спасибо за объяснение. Для MultipleBagFetchException мой модульный тест в порядке. Я знаю об этом исключении, но буду хранить список до тех пор, пока не понадобится набор, потому что мне могут понадобиться дубликаты, у меня еще нет полного понимания природы сохраняемых данных, индексированный список также может ответить на эту проблему.
Ассаляму алейкум, можно адрес вашего репозитория? Я хотел бы попробовать это в моем местном.