У меня имеются 2 сущности связанные между собой
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity(name = "ClientFromJwrToken")
@Table(name = "clientFromJwrToken")
public class ClientFromJwrToken extends BaseCrudEntity<ClientFromJwrToken, Long> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String token;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "client_jwt_token_monitoring_objects",
joinColumns = {@JoinColumn(name = "client_jwt_token_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "monitoring_object_id", referencedColumnName = "id")})
private Set<MonitoringObject> monitoringObjects;
}
И вторая :
@Data
@Table(name = "monitoring_object")
@Entity(name = "MonitoringObject")
public class MonitoringObject extends BaseCrudEntity<MonitoringObject, Long> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "monitoringobject", targetEntity = GPSLocationPosition.class)
@JsonIgnore
@Basic(fetch=FetchType.LAZY)
@LazyCollection(LazyCollectionOption.TRUE)
private Set<GPSLocationPosition> positions;
}
Репозитарии
@Repository
public interface ClientFromJwrTokenRepository extends BaseRepository<ClientFromJwrToken, Long> {
ClientFromJwrToken findByToken(final String token);
}
@Repository("MonitoringObjectRepository")
public interface MonitoringObjectRepository extends BaseRepository<MonitoringObject, Long> {
}
И сервисы :
@Service
@RequiredArgsConstructor
public class ClientFromJwrTokenServiceImpl extends BaseCrudService<ClientFromJwrToken, Long> implements ClientFromJwrTokenService {
private final ClientFromJwrTokenRepository clientFromJwrTokenRepository;
@Override
public BaseRepository<ClientFromJwrToken, Long> getRepository() {
return clientFromJwrTokenRepository;
}
@Override
public ClientFromJwrToken getByToken(String token) {
return clientFromJwrTokenRepository.findByToken(token);
}
}
В своём контроллере я пытаюсь получить
ClientFromJwrToken client = clientFromJwrTokenService.getByToken("токен");
и не взирая на то что указано использовать стратегию FetchType.LAZY (я прочитал что это ни чего не значит для JPA это просто напоминаение для конкретной реализации, но как тогда то ?), выполняется куча запросов и вытягиваются все записи коллекции MonitoringObject.positions, хотя я ни где явно я не делаю get значений этой коллекции (в дальнейшем делаю но в процессе отладки я остановился на минимальном коде).
я также пробовал подключить плагин и это не дало результата
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>5.0.11.Final</version>
<executions>
<execution>
<configuration>
<enableLazyInitialization>true</enableLazyInitialization>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
В итоге мне хотелось бы получить неообходимый MonitoringObject и не выполняя SELECT к базе данный сделать INSERT новых значений в коллекцию Set positions;
Подскажите пожулуйста как это делается правильно, потому как в текущей реализации при большом количестве записей в positions приложение падает по OutOfMemory т.к. всегда делает эти SELECT запросы при любой моей попытке хоть как то работать с этими сущностями.
Используя ответ Viktor Podoprigo решил свою проблему используя EntityGraph
NamedEntityGraph(
name = "clientFromJwrToken-entity-graph-with-monitoringObjects-name",
attributeNodes = {
@NamedAttributeNode("id"),
@NamedAttributeNode("name"),
@NamedAttributeNode("token"),
@NamedAttributeNode(value = "monitoringObjects", subgraph = "monitoringObjects-subgraph"),
},
subgraphs = {
@NamedSubgraph(
name = "monitoringObjects-subgraph",
attributeNodes = {
@NamedAttributeNode("name")
}
)
}
)
public class ClientFromJwrToken extends BaseCrudEntity<ClientFromJwrToken, Long> {
...
и использовал данный граф применив анатацию к методу который осуществляет запрос данных с поиском по полю в моём репозитарии
@Repository
public interface ClientFromJwrTokenRepository extends BaseRepository<ClientFromJwrToken, Long> {
@EntityGraph(value = "clientFromJwrToken-entity-graph-with-monitoringObjects-name", type = EntityGraph.EntityGraphType.LOAD)
ClientFromJwrToken findByToken(final String token);
}
и не взирая на то что указано использовать стратегию FetchType.LAZY (я прочитал что это ни чего не значит для JPA это просто напоминаение для конкретной реализации, но как тогда то ?), выполняется куча запросов и вытягиваются все записи коллекции MonitoringObject.positions
@OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "monitoringobject")
Если Вы пишите fetch=FetchType.LAZY
, то для Hibernate это конкретное указание, что коллекция lazy и ее загружать не надо.
Как работают lazy-коллекции
Если у Вас нет session (persistent context), то при обращении к такой коллекции выбрасывается LazyInitializationException.
Если session есть
При обращении к такой коллекции Hibernate автоматически ее подгружает. Это самое простое.
Есть одна неочевиданая деталь. При обращении к любому методу объекта, обернутого Hibernate Proxy, Hibernate тоже будет пытаться подгрузить lazy-колеекцию, если есть session.
MonitoringObject
обернут Hibernate Proxy, так как у него есть lazy-коллекция. Hibernate не может контролировать поля, к которым возможно обращение внутри вызываемого метода. Поэтому, он при обращении к любому методу, даже не имеющему отношения к lazy-полям, загружает все lazy-поля. Единственное исключение — это lazy-поля, при обращении к getter такого поля, загружается только это поле.
Например, у вас есть аннотация @Data
. Эта аннотация добавляет toString()
метод. Если Вы выводите в лог MonitoringObject
, то Hibernate загрузит все его lazy-поля. Так же Hibernate загрузит lazy-поля, если Вы просматриваете объект в отладчике.
Скорее всего у Вас lazy-поля загружаются в резултате обращения к каким-то методам объекта.
В итоге мне хотелось бы получить неообходимый MonitoringObject и не выполняя SELECT к базе данный сделать INSERT новых значений в коллекцию Set positions;
Проще сделать что-то вроде этого
@Table
class GPSLocationPosition {
@ManyToOne
MonitoringObject monitoring;
}
final long monitoringPid = someValue;
MonitoringObject monitoring = new MonitoringObject();
monitoring.setPid(monitoringPid);
GPSLocationPosition position = new GPSLocationPosition();
position.setMonitoring(monitoring);
save(position);
Если у Вас есть monitoring
, то вы сразу можете его установить.
Так же можно использовать session.load()
, entityManager.getReference()
.
Для получения proxy по pid.
https://stackoverflow.com/a/35777910/3405171
Вобще лучше экспериментировать с mapping в рамках простых консольных приложений. С чистым Hibernate без Spring
https://github.com/v-ladynev/fluent-hibernate-mysql/blob/master/src/com/github/fluent/hibernate/example/mysql/MySqlExample.java
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Стоит задача собрать в мапу студентов с самой большой оценкой из каждого классаКласс студент
Я обычно использую VARCHAR(255) и в java пишу preparedStatementsetString(
В SVG тултип реализуется с помощью парного тега <title> Текст подсказки </title>