I have one issue that more experienced users might help me with.
I have two classes, Company and 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;
}
As you can see, I have a couple of ManyToOne relationships and Unit Entity is self-referencing.
I also added few @JsonIgnore annotations for fields which I don't want to be output on REST API request. For example, when querying for Units, I don't want whole Company object and all sub-units to be returned. However, I would like to return parentId for a Unit. Also, when adding new Unit through REST API, I want a client to be able to set Unit's parent Unit by setting "parentId":<id>.
So I figured that the correct way is to add another field "parentId" in Unit class which is annotated with @Transient. This way, it doesn't get saved to the database, but it will be output when queried.
@Transient
private long parentId;
When returning a Unit object it is sufficient to write a getter like this:
public long getParentId()
{
if (parent != null)
{
this.parentId = parent.getId();
}
return this.parentId;
}
and when persisting a new Unit:
Unit parent = unitService.getUnit(newUnit.getParentId()); // returns null if there's no Unit with supplied ID
newUnit.setParent(parent);
unitRepository.save(newUnit);
All that's left is to extend CrudRepository with:
Iterable<Unit> findByParentId(long parentId);
when I want to get all Unit's sub-units (http://localhost:8080/company/1/units/2/subunits).
However, the line above throws:
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]
It seems that for self-referencing entities Spring is unable to differentiate between transient field parentId and non-transient parent.getId() when creating CrudRepository's method findByParentId(long id).
I know this can be easily fixed by renaming transient field, but I wanted to know if there's more elegant way to solve this? Maybe someone more experienced figured some other way of dealing with situations like this? I have a feeling that I should me doing these things a bit differently...
Thanks!