The problem is solved, but with terrible performance.
What I failed to describe is that the SwitchPort always has zero or one NetworkCable. I can't map it as a normal one-to-many though, since one of two columns in the table might be set. Example:
We have four types of cables here
Code:
id | connectiontype | computer | outlet | port | downlink
--------+----------------+----------+--------+-------+----------
108526 | 1 | 11111 | 22222 | |
This is a cable from a computer to an outlet. Rather straight forward, both ends are Sets.
Code:
id | connectiontype | computer | outlet | port | downlink
--------+----------------+----------+--------+-------+----------
108447 | 3 | | 33333 | 44444 |
The outlet in turn is connected to a port. The outlet-end is a Set but the id in the port column may only appear once*.
Code:
id | connectiontype | computer | outlet | port | downlink
--------+----------------+----------+--------+-------+----------
108518 | 2 | 11112 | | 44445 |
This is the case we have in server rooms. The computer is connected directly to the port in the switch. The computer-end is a Set but the id in the port column may only appear once*.
Code:
id | connectiontype | computer | outlet | port | downlink
--------+----------------+----------+--------+-------+----------
92941 | 4 | | | 44446 | 44447
This is case where we have uplink/downlink cables between switches. Here we have two SwitchPorts (44446 and 44447) that are connected with the same cable (a NetworkCablePortPort) but they're referenced from two separate columns.
I currently handle both cases (both columns) by implementing LifeCycle in SwitchPort and the getNetworkCable-method looks like this:
Code:
public NetworkCable getNetworkCable() {
if (this.networkCable == null) {
if (!this.cableLoadAttemptPerformed) {
this.cableLoadAttemptPerformed = true;
try {
this.networkCable = CommonTasks.getSwitchPortsNetworkCable(
this.session,
this.getId());
} catch (HibernateException he) {
throw new LazyInitializationException(
"Failed to lazily load network cable", he);
}
}
}
return this.networkCable;
}
and the method in CommonTasks looks like this:
Code:
public static NetworkCable getSwitchPortsNetworkCable(Session hibernate, int portId)
throws HibernateException
{
Logger.log(Logger.DEBUG, "getSwitchPortsNetworkCable(" + portId + ")");
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// prepare query
String query = "SELECT c.id FROM NetworkCables c WHERE c.port=? OR c.downlink=?;";
pstmt = hibernate.connection().prepareStatement(query);
pstmt.setInt(1, portId);
pstmt.setInt(2, portId);
// execute, get id
Integer id = null;
rs = pstmt.executeQuery();
if (rs.next()) {
id = new Integer(rs.getInt(1));
if (rs.next()) {
silentlyClose(null, pstmt, rs);
throw new HibernateException(
"More than one cable found for port with id " + portId + "!");
}
}
// close up
rs.close();
pstmt.close();
// get NetworkCable object and return it
if (id == null) {
return null;
} else {
return (NetworkCable) hibernate.load(NetworkCable.class, id);
}
} catch (SQLException sqle) {
silentlyClose(null, pstmt, rs);
Logger.logException(Logger.DEBUG, sqle);
throw new HibernateException("Failed to find NetworkCable.", sqle);
} catch (HibernateException he) {
silentlyClose(null, pstmt, rs);
Logger.logException(Logger.DEBUG, he);
throw new HibernateException("Failed to load new NetworkCable.", he);
}
}
That's the current implementation but I'm thinking of changing it to map up the SwitchPorts.cable with inverse reference column port and in the getNetworkCable method only look for a cable in the downlink-column if this.cable is null.