#spring #spring-boot #spring-data-jpa
#spring #spring-boot #spring-data-jpa
Вопрос:
У меня есть одна проблема, с которой мне могли бы помочь более опытные пользователи.
У меня есть два класса: Company и Unit:
@Entity(name="Company")
public class Company
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
}
@Entity(name="Unit")
public class Unit
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
@JsonIgnore
@ManyToOne(optional = false)
private Company company;
@JsonIgnore
@OneToMany(mappedBy = "parent")
private List<Unit> subunits = new ArrayList<Unit>();
@JsonIgnore
@ManyToOne
private Unit parent;
}
Как вы можете видеть, у меня есть пара отношений ManyToOne, и объект Unit ссылается на себя.
Я также добавил несколько аннотаций @JsonIgnore для полей, которые я не хочу выводить по запросу REST API. Например, при запросе единиц измерения я не хочу, чтобы возвращался весь объект Company и все подразделения. Тем не менее, я хотел бы вернуть ParentID для единицы. Кроме того, при добавлении нового модуля через REST API я хочу, чтобы клиент мог установить родительский модуль Unit, установив «ParentID»:<id>.
Итак, я решил, что правильный способ — добавить еще одно поле «ParentID» в Unit class, которое помечено @Transient. Таким образом, оно не сохраняется в базе данных, но будет выводиться при запросе.
@Transient
private long parentId;
При возврате объекта Unit достаточно написать средство получения, подобное этому:
public long getParentId()
{
if (parent != null)
{
this.parentId = parent.getId();
}
return this.parentId;
}
и при сохранении нового блока:
Unit parent = unitService.getUnit(newUnit.getParentId()); // returns null if there's no Unit with supplied ID
newUnit.setParent(parent);
unitRepository.save(newUnit);
Все, что осталось, это расширить CrudRepository с помощью:
Iterable<Unit> findByParentId(long parentId);
когда я хочу получить все подразделения Unit (http://localhost:8080/company/1/units/2/subunits ).
Однако строка выше выдает:
Error creating bean with name 'unitRepository': FactoryBean threw exception on object creation;
nested exception is java.lang.IllegalArgumentException: Failed to create query for method public
abstract java.lang.Iterable c.a.f.m.unit.UnitRepository.findByParentId(long)! Unable to locate
Attribute with the the given name [parentId] on this ManagedType [c.a.f.m.unit.Unit]
Похоже, что для объектов, ссылающихся на себя, Spring не может различать родительский идентификатор переходного поля и непереходящий parent.getId() при создании метода CrudRepository findByParentId (длинный идентификатор).
Я знаю, что это можно легко исправить, переименовав поле transient, но я хотел знать, есть ли более элегантный способ решить эту проблему? Может быть, кто-то более опытный придумал какой-то другой способ справиться с подобными ситуациями? У меня такое чувство, что мне следовало бы делать эти вещи немного по-другому…
Спасибо!
Ответ №1:
Хорошо, я должен был ознакомиться с документацией, прежде чем публиковать вопрос.
Здесь описано, как Spring переводит имена методов в реальные запросы. Все, что мне нужно сделать, это добавить символ подчеркивания, чтобы указать Spring, где разделить свойства. Итак, мой метод findByParentId должен быть назван findByParent_Id. Таким образом, Spring проигнорирует свойство ParentID и перейдет к parent.id собственность.
С уважением!