Caching naturalID, Hibernate always hit the database (mapping problem)

#1

Hi everybody,

I am facing an issue with Hibernate caching. I have one query which correctly hit the database only the first time. I trigger this query every 15 seconds and every 15 seconds I have an hit to database with this query (first and second level cache are enabled)

Hibernate:
[2019-03-11 13:31:14] [info] /* get current natural-id->entity-id state lt.model.Bridge */ select
[2019-03-11 13:31:14] [info] bridge_.ID as ID1_4_
[2019-03-11 13:31:14] [info] from
[2019-03-11 13:31:14] [info] leitfeld3.lt_bridge bridge_
[2019-03-11 13:31:14] [info] where
[2019-03-11 13:31:14] [info] bridge_.natsID=?

These are the classes involved:
Device class:

package lt.model;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NaturalIdCache;

import com.google.gson.annotations.Expose;

import lt.database.LTDBConfig;

@Entity(name = “Device”)
@Table(name = “lt_device”, schema = LTDBConfig.LTSchema)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@NaturalIdCache
public class Device extends LTTable {
private static final long serialVersionUID = 3028650185302870665L;

@Expose
@NaturalId(mutable = false)
@ManyToOne
@Fetch(FetchMode.JOIN)
@JoinColumn(name = "deploymentID", nullable = false)
private Deployment deployment;

@Expose
@NaturalId(mutable = false)
@Column(name = "name", length = 100, nullable = false)
protected String name;

@Expose
@ManyToOne
@Fetch(FetchMode.JOIN)
@JoinColumn(name = "familyID", nullable = false)
private Family family;

@Expose
@ManyToOne
@Fetch(FetchMode.JOIN)
@JoinColumn(name = "nodemodelID", nullable = false)
private NodeModel nodeModel;

@Expose
@ManyToOne
@Fetch(FetchMode.JOIN)
@JoinColumn(name = "bridgeID", nullable = false)
private Bridge bridge;

@Expose
@Column(name = "description", columnDefinition = "TEXT", nullable = true)
private String description;

@Expose
@Column(name = "isonmap", nullable = false)
private boolean isonmap;

@Expose
@Column(name = "online", nullable = false)
private boolean online;

@Expose
@Column(name = "linktype", length = 100, nullable = true)
private String linktype;

@Expose
@ManyToMany(cascade = { 
		CascadeType.PERSIST, 
		CascadeType.MERGE,
		CascadeType.REFRESH},
		fetch = FetchType.LAZY)
@JoinTable(name = "lt_dglink", schema = LTDBConfig.LTSchema,
	joinColumns = @JoinColumn(name = "deviceID", nullable = false, updatable = false),
	inverseJoinColumns = @JoinColumn(name = "groupID", nullable = false, updatable = false))
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<DeviceGroup> deviceGroups = new HashSet<>();

public Deployment getDeployment() {
	return deployment;
}

public void setDeployment(Deployment deployment) {
	this.deployment = deployment;
}

public String getName() {
	return name;
}

public void setName(String name) {
	this.name = name;
}

public Family getFamily() {
	return family;
}

public void setFamily(Family family) {
	this.family = family;
}

public NodeModel getNodeModel() {
	return nodeModel;
}

public void setNodeModel(NodeModel nodeModel) {
	this.nodeModel = nodeModel;
}

public String getDescription() {
	return description;
}

public void setDescription(String description) {
	this.description = description;
}

public boolean isIsonmap() {
	return isonmap;
}

public void setIsonmap(boolean isonmap) {
	this.isonmap = isonmap;
}

public boolean isOnline() {
	return online;
}

public void setOnline(boolean online) {
	this.online = online;
}

public Bridge getBridge() {
	return bridge;
}

public void setBridge(Bridge bridge) {
	this.bridge = bridge;
}

public Set<DeviceGroup> getDeviceGroups() {
	return deviceGroups;
}

public void setDeviceGroups(Set<DeviceGroup> deviceGroups) {
	this.deviceGroups = deviceGroups;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || !getClass().equals(o.getClass())) return false;
    Device device = (Device) o;
    return Objects.equals(name, device.name) &&
    		Objects.equals(deployment, device.deployment);
}

@Override
public int hashCode() {
    return Objects.hash(deployment+name);
}

}

Bridge class:
package lt.model;

import java.util.Objects;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NaturalIdCache;
import org.hibernate.annotations.Type;
import org.postgresql.geometric.PGpoint;

import com.google.gson.annotations.Expose;

import lt.database.LTDBConfig;

@Entity(name = “Bridge”)
@Table(name = “lt_bridge”, schema = LTDBConfig.LTSchema)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@NaturalIdCache
public class Bridge extends LTTable {

private static final long serialVersionUID = 3328695709303986526L;

@Expose
@Column(name = "name", length = 100, nullable = false)
private String name;

@Expose
@Column(name = "description", columnDefinition = "TEXT", nullable = true)
private String description;

@Expose
@ManyToOne
@Fetch(FetchMode.JOIN)
@JoinColumn(name = "nodemodelID", nullable = false)
private NodeModel nodeModel;

@Expose
@NaturalId(mutable = false)
@Column(name = "natsID", unique = true, length = 50, nullable = false, updatable = false)
private String natsID;

@Expose
@Column(name = "protocol", length = 100, nullable = false)
private String protocol;

@Expose
@Column(name = "address", length = 100, nullable = false)
private String address;

@Expose
@Column(name = "port", nullable = false)
private int port;

@Expose
@Column(name = "url", length = 100, nullable = true)
private String url;

@Expose
@Column(name = "login", length = 100, nullable = false)
private String login;

@Expose
@Column(name = "pwd", length = 100, nullable = false)
private String pwd;

@Column(name = "location", nullable = false)
@Type(type = "PGpoint")
private PGpoint location;

@Expose
@Column(name = "isonmap",  nullable = false)
private boolean isonmap;

@Expose
@Column(name = "online",  nullable = false)
private boolean online;

public Bridge() {}

public String getName() {
	return name;
}

public void setName(String name) {
	this.name = name;
}

public String getNatsID() {
	return natsID;
}

public void setNatsID(String natsID) {
	this.natsID = natsID;
}

public String getDescription() {
	return description;
}

public void setDescription(String description) {
	this.description = description;
}

public NodeModel getNodeModel() {
	return nodeModel;
}

public void setNodeModel(NodeModel nodeModel) {
	this.nodeModel = nodeModel;
}

public String getProtocol() {
	return protocol;
}

public void setProtocol(String protocol) {
	this.protocol = protocol;
}

public String getAddress() {
	return address;
}

public void setAddress(String address) {
	this.address = address;
}

public int getPort() {
	return port;
}

public void setPort(int port) {
	this.port = port;
}

public String getUrl() {
	return url;
}

public void setUrl(String url) {
	this.url = url;
}

public String getLogin() {
	return login;
}

public void setLogin(String login) {
	this.login = login;
}

public String getPwd() {
	return pwd;
}

public void setPwd(String pwd) {
	this.pwd = pwd;
}

public PGpoint getLocation() {
	return location;
}

public void setLocation(PGpoint location) {
	this.location = location;
}

public boolean isIsonmap() {
	return isonmap;
}

public void setIsonmap(boolean isonmap) {
	this.isonmap = isonmap;
}

public boolean isOnline() {
	return online;
}

public void setOnline(boolean online) {
	this.online = online;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || !getClass().equals(o.getClass())) return false;
    Bridge bridge = (Bridge) o;
    return Objects.equals(natsID, bridge.natsID);
}

@Override
public int hashCode() {
    return Objects.hash(natsID);
} 

}

These is the query:

			sql = "SELECT B FROM Bridge B INNER JOIN Device D ON D.bridge = B WHERE D.name = :pName AND D.deployment = :pDeployment AND D.bridge = B";
			logger.trace("Query SQL is " + sql);
			logger.trace("Parameters are: Deployment ID = " + depId + " Device name = " + nodeId);

			Bridge bridge = (Bridge) session.createQuery(sql)
					.setParameter("pName", nodeId)
					.setParameter("pDeployment", dep)
					.setCacheable(true)
					.setCacheRegion(LTCacheRegion.QUERY_LONGTIME.toString())
					.getSingleResult();

These are the query; the first one is triggered only once, the second one is triggered every time.

Hibernate:
[2019-03-11 12:45:25] [info] /* SELECT
[2019-03-11 12:45:25] [info] B
[2019-03-11 12:45:25] [info] FROM
[2019-03-11 12:45:25] [info] Bridge B
[2019-03-11 12:45:25] [info] INNER JOIN
[2019-03-11 12:45:25] [info] Device D
[2019-03-11 12:45:25] [info] ON D.bridge = B
[2019-03-11 12:45:25] [info] WHERE
[2019-03-11 12:45:25] [info] D.name = :pName
[2019-03-11 12:45:25] [info] AND D.deployment = :pDeployment
[2019-03-11 12:45:25] [info] AND D.bridge = B / select
[2019-03-11 12:45:25] [info] bridge0_.ID as ID1_4_,
[2019-03-11 12:45:25] [info] bridge0_.insert_date as insert_d2_4_,
[2019-03-11 12:45:25] [info] bridge0_.insert_user as insert_u3_4_,
[2019-03-11 12:45:25] [info] bridge0_.update_date as update_d4_4_,
[2019-03-11 12:45:25] [info] bridge0_.update_user as update_u5_4_,
[2019-03-11 12:45:25] [info] bridge0_.address as address6_4_,
[2019-03-11 12:45:25] [info] bridge0_.description as descript7_4_,
[2019-03-11 12:45:25] [info] bridge0_.isonmap as isonmap8_4_,
[2019-03-11 12:45:25] [info] bridge0_.location as location9_4_,
[2019-03-11 12:45:25] [info] bridge0_.login as login10_4_,
[2019-03-11 12:45:25] [info] bridge0_.name as name11_4_,
[2019-03-11 12:45:25] [info] bridge0_.natsID as natsID12_4_,
[2019-03-11 12:45:25] [info] bridge0_.nodemodelID as nodemod18_4_,
[2019-03-11 12:45:25] [info] bridge0_.online as online13_4_,
[2019-03-11 12:45:25] [info] bridge0_.port as port14_4_,
[2019-03-11 12:45:25] [info] bridge0_.protocol as protoco15_4_,
[2019-03-11 12:45:25] [info] bridge0_.pwd as pwd16_4_,
[2019-03-11 12:45:25] [info] bridge0_.url as url17_4_
[2019-03-11 12:45:25] [info] from
[2019-03-11 12:45:25] [info] leitfeld3.lt_bridge bridge0_
[2019-03-11 12:45:25] [info] inner join
[2019-03-11 12:45:25] [info] leitfeld3.lt_device device1_
[2019-03-11 12:45:25] [info] on (
[2019-03-11 12:45:25] [info] device1_.bridgeID=bridge0_.ID
[2019-03-11 12:45:25] [info] )
[2019-03-11 12:45:25] [info] where
[2019-03-11 12:45:25] [info] device1_.name=?
[2019-03-11 12:45:25] [info] and device1_.deploymentID=?
[2019-03-11 12:45:25] [info] and device1_.bridgeID=bridge0_.ID
[2019-03-11 12:45:25] [info] Hibernate:
[2019-03-11 12:45:25] [info] /
get current natural-id->entity-id state lt.model.Bridge */ select
[2019-03-11 12:45:25] [info] bridge_.ID as ID1_4_
[2019-03-11 12:45:25] [info] from
[2019-03-11 12:45:25] [info] leitfeld3.lt_bridge bridge_
[2019-03-11 12:45:25] [info] where
[2019-03-11 12:45:25] [info] bridge_.natsID=?

Thanks in advance for your help.

#2

Problem solved, my mistake.