I'm trying to build a Spring/Hibernate application on top of an existing database structure and having some problems when trying to map my Java objects to existing data using annotations. The code below is an amalgamation from a number of tutorials and general web trawling, being new to both Spring and Hibernate this is turning into quite a steep learning curve!
The actual problem is that when the servlet returns the JSON back to JS in my page, it seems to be creating an infinate recursion problem that manages to take down Tomcat(!!).
When I make the JS call to fetch JSON representing a Route I want to get back the Route fields along with a List/Set of which checkpoints a route uses rather than this massive recursion I'm getting.
Returned JSON:
[{"name":"External Test Patrol","duration":-2209161600000,"maxTime":-2209161000000,"minTime":-2209161600000,"oid":15138,"routePoints":[{"oid":25703,"pointNo":4,"route":{"name":"External Test Patrol","duration":-2209161600000,"maxTime":-2209161000000,"minTime":-2209161600000,"oid":15138,"routePoints":[{"oid":25703,"pointNo":4,"route":{"name":"External Test Patrol","duration":-2209161600000,"maxTime":-2209161000000,"minTime":-2209161600000,"oid":15138,"routePoints":[{"oid":25703,"pointNo":4,"route":{"name":"External Test Patrol","duration":-2209161600000,"maxTime":-2209161000000,"minTime":-2209161600000,"oid":15138,"routePoints":[{"oid":25703,"pointNo":4,"route":{"name":"External Test Patrol","duration":-2209161600000,"maxTime":-2209161000000,"minTime":-2209161600000,"oid":15138,"routePoints":[{"oid":25703,"pointNo":4,"route":{"name":"External Test Patrol","duration":-2209161600000,"maxTime":-2209161000000,"minTime":-2209161600000,"oid":15138,"routePoints":[{"oid":25703,"pointNo":4,"route":{"name":"External Test Patrol","duration":-2209161600000,"maxTime":-2209161000000,"minTime":-2209161600000,"oid":15138,"routePoints":[{"oid":25703,"pointNo":4,"route":{"name":"External Test Patrol","duration":-2209161600000,"maxTime":-2209161000000,"minTime":-2209161600000,"oid":15138,"routePoints":[{"oid":25703,"pointNo":4,"route" ............. etc etc
Both Spring and Hibernate are recent (summer 2011) versions.
The tables of interest in the DB:
ROUTES {OID, name, <other fields of no significance>}
CHECKPOINTS {OID, name, <other dull fields of no interest>}
ROUTEPOINTS {OID, route*, checkpoint*, pointno} //where route is a FK ref to ROUTES.OID and checkpoint is FK ref to CHECKPOINTS.OID
A route may have several checkpoints, a checkpoint may feature in several routes. So a manytomany relationship.
If anyone could suggest where this code may be going wrong or even point me at a better way of implementing such a relationship I'd be very grateful. The complexity seems to come about because the many-to-many mapping table in the DB also has other columns too.
Some Java:
Route.java represents a Route object
Code:
@Entity
@Table(name="ROUTES")
public class Route extends GuardianObjectAbstract implements Serializable{
private static final long serialVersionUID = 1L;
public String toString(){
return "OID:"+OID+" name:"+name+ " size:"+getRoutePoints().size();
}
@Id
@Column(name="OID")
@NotFound(action=NotFoundAction.EXCEPTION)
private Integer OID = -1;
public Integer getOID() {return this.OID;}
public void setOID(Integer id) {this.OID = id;}
@JsonDeserialize(contentAs=RoutePoint.class)
@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.route")
private Set<RoutePoint> routePoints = new HashSet<RoutePoint>();
public Set<RoutePoint> getRoutePoints() { return this.routePoints; }
public void setRoutePoints(Set<RoutePoint> routepoints) {
this.routePoints = routepoints;
}
//more getters and setters for simple fields follow, not included for brevity
}
CheckPoint.java
Code:
@Entity
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE)
@Table(name="CHECKPOINTS")
public class Checkpoint extends GuardianObjectAbstract implements Serializable{
@Id
@Column(name="OID")
private Integer OID = -1;
public Integer getOID() {return this.OID;}
public void setOID(Integer id) {this.OID=id;}
@JsonDeserialize(contentAs=RoutePoint.class)
@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.checkpoint")
private Set<RoutePoint> routePoints = new HashSet<RoutePoint>();
public Set<RoutePoint> getRoutePoints() { return this.routePoints; }
public void setRoutePoints(Set<RoutePoint> routePoints) {
this.routePoints = routePoints;
this.init();
}
@Column(name="NAME")
@NotFound(action=NotFoundAction.IGNORE)
private String name = "";
public String getName() {return name;}
public void setName(String name) {this.name = name;}
//left out bunch of getters/setters for brevity
}
RoutePoint.java - the mapping table
Code:
@Entity
@Table(name = "ROUTEPOINTS")
@AssociationOverrides({
@AssociationOverride(name = "pk.route", joinColumns = @JoinColumn(name = "ROUTE")),
@AssociationOverride(name = "pk.checkpoint", joinColumns = @JoinColumn(name = "CHECKPOINT"))
})
public class RoutePoint extends GuardianObjectAbstract implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="OID")
@NotFound(action=NotFoundAction.EXCEPTION)
private Integer OID = -1;
public Integer getOID() {return this.OID;}
public void setOID(Integer id) {this.OID = id;}
public void init(){
//
}
@EmbeddedId
private RouteCheckPointPK pk = new RouteCheckPointPK();
public RouteCheckPointPK getPk() { return pk; }
public void setPk(RouteCheckPointPK pk) { this.pk = pk; }
@Transient
public Checkpoint getCheckpoint() { return (Checkpoint)getPk().getCheckpoint(); }
public void setCheckpoint(Checkpoint checkpoint) { getPk().setCheckpoint(checkpoint);
@Transient
public Route getRoute() { return getPk().getRoute();}
public void setRoute(Route r) { getPk().setRoute(r); }
@Column(name="POINTNO")
private Integer pointNo = -1;
public Integer getPointNo() {return this.pointNo;}
public void setPointNo(Integer pNo){ this.pointNo = pNo; }
}
RouteCheckPointPK.java
Code:
@Embeddable
public class RouteCheckPointPK implements Serializable {
private static final long serialVersionUID = 1L;
@ManyToOne(fetch = FetchType.LAZY)
private Route route;
public Route getRoute() {return route;}
public void setRoute(Route r) {this.route = r;}
@ManyToOne(fetch = FetchType.LAZY)
private Checkpoint checkpoint;
public Checkpoint getCheckpoint() {return checkpoint;}
public void setCheckpoint(Checkpoint cp) {this.checkpoint = cp;}
}