Lazy-загруженный столбец @ManyToOne переопределяется в нуль после вызова @ Repository.save () - PullRequest
0 голосов
/ 28 декабря 2018

В моем проекте Spring Boot у меня есть следующее поле отношения @ManyToOne, ссылающееся на тот же @Entity, что и у родителя.

@Entity
@Table(name="workspace")
public class Workspace extends AbstractEntity {
    @NaturalId
    @Column(nullable=false, unique=true)
    private String code;

    @ManyToOne(fetch=FetchType.LAZY, optional=true)
    @LazyToOne(LazyToOneOption.NO_PROXY)
    @JoinColumn(name="default_workspace", referencedColumnName="code")
    private Workspace parent;

    private String name;
    private String shortName;
}

Режим отложенной загрузки работает, как и ожидалось.Однако каждый раз, когда я вызываю @Repository.save() для этого @Entity, поле default_workspace будет установлено на NULL.

ОБНОВЛЕНИЕ: Включите некоторые конфигурации, запрошенные в комментарии

Чтобы включить отложенную загрузку с помощью Hibernate, я просто включил плагин hibernate-enhance-maven-plugin в свой pom.xml.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

  <groupId>com.ft</groupId>
  <artifactId>com.ft.admin</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.ft</groupId>
      <artifactId>com.ft.common</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.hibernate.orm.tooling</groupId>
        <artifactId>hibernate-enhance-maven-plugin</artifactId>
        <version>${hibernate.version}</version>
        <executions>
          <execution>
            <configuration>
              <failOnError>true</failOnError>
              <enableLazyInitialization>true</enableLazyInitialization>
            </configuration>
            <goals>
              <goal>enhance</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>

Для выполнения процедур CRUD на моем Workspace я использую следующее @Repository.

@Repository
public interface WorkspaceRepo extends CrudRepository<Workspace, Integer> {
    Optional<Workspace> findByCode(String workspaceCode);
}

По умолчанию в Spring Boot граница транзакции остается в пределахкаждый звонок @Repository.Следовательно, чтобы иметь возможность доступа к моему загруженному ленивому полю без использования LazyInitializationException: no Session, я добавил @Transactional к своему @Service, чтобы расширить границу транзакции следующим образом.

@Service
@Transactional
public class AppAdminService {
    @Autowired
    private WorkspaceRepo workspaceRepo;

    public void updatePsyWorkspace() {
        Workspace childWorkspace = workspaceRepo.findByCode("PSY").get();
        childWorkspace.setShortName("PSY SHORT");
        workspaceRepo.save(childWorkspace);
    }
}

Когда я выполняю WorkspaceRepo.findByCode(code), я возвращаю дочернюю запись с допустимым ненулевым parent.Однако всякий раз, когда я выполняю WorkspaceRepo.save(childWorkspace), parent устанавливается в NULL, а столбец default_workspace становится NULL в базе данных, даже если я только обновил поле ShortName, как показано в примере выше.

ОБНОВЛЕНИЕ: временное решение

В качестве временного решения мне пришлось включить простое поле String для столбца default_workspace и исключить parentполе из операций Hibernate следующим образом.

@Entity
@Table(name="workspace")
@Getter @Setter
public class Workspace extends AbstractEntity {
    ...

    @ManyToOne(fetch=FetchType.LAZY, optional=true)
    @LazyToOne(LazyToOneOption.NO_PROXY)
    @JoinColumn(
        name="default_workspace", 
        referencedColumnName="code",
        insertable=false,
        updatable=false)
    private Workspace parent;

    @Getter(AccessLevel.NONE)
    @Setter(AccessLevel.NONE)
    @Column(name="default_workspace")
    private String defaultWorkspace;

    public void setParent(Workspace parent) {
        this.parent           = parent;
        this.defaultWorkspace = (parent != null) ? parent.getCode() : null;
    }

    ...
}

Используя AccessLevel.NONE, я полностью скрываю это поле от внешнего мира.Любые вызовы для обновления поля parent дополнительно обновят поле defaultWorkspace.Даже если это решение работает, оно мне не подходит.

Буду очень признателен, если вы покажете мне лучший способ достичь той же цели.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...