package org.javers.repository.sql.repositories;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.Optional;
import org.javers.core.json.JsonConverter;
import org.javers.core.metamodel.object.GlobalId;
import org.javers.core.metamodel.object.InstanceId;
import org.javers.core.metamodel.object.UnboundedValueObjectId;
import org.javers.core.metamodel.object.ValueObjectId;
import org.javers.repository.sql.SqlRepositoryConfiguration;
import org.javers.repository.sql.schema.SchemaNameAware;
import org.javers.repository.sql.schema.TableNameProvider;
import org.polyjdbc.core.PolyJDBC;
import org.polyjdbc.core.query.InsertQuery;
import org.polyjdbc.core.query.SelectQuery;
import static org.javers.repository.sql.PolyUtil.queryForOptionalLong;
import static org.javers.repository.sql.schema.FixedSchemaFactory.*;
public class GlobalIdRepository extends SchemaNameAware {
private final PolyJDBC polyJdbc;
private JsonConverter jsonConverter;
private final boolean disableCache;
private Cache<GlobalId, Long> globalIdPkCache = CacheBuilder.newBuilder()
.maximumSize(1000)
.build();
public GlobalIdRepository(PolyJDBC javersPolyjdbc, TableNameProvider tableNameProvider, SqlRepositoryConfiguration configuration) {
super(tableNameProvider);
this.polyJdbc = javersPolyjdbc;
this.disableCache = configuration.isGlobalIdCacheDisabled();
}
public long getOrInsertId(GlobalId globalId) {
Optional<Long> pk = findGlobalIdPk(globalId);
return pk.isPresent() ? pk.get() : insert(globalId);
}
public void evictCache() {
globalIdPkCache.invalidateAll();
}
public int getGlobalIdPkCacheSize() {
return (int)globalIdPkCache.size();
}
/**
* cached
*/
public Optional<Long> findGlobalIdPk(GlobalId globalId) {
if (disableCache){
return findGlobalIdPkInDB(globalId);
}
Long foundPk = globalIdPkCache.getIfPresent(globalId);
if (foundPk != null){
return Optional.of(foundPk);
}
Optional<Long> fresh = findGlobalIdPkInDB(globalId);
if (fresh.isPresent()){
globalIdPkCache.put(globalId, fresh.get());
}
return fresh;
}
private Optional<Long> findGlobalIdPkInDB(GlobalId globalId) {
SelectQuery query = polyJdbc.query().select(GLOBAL_ID_PK).from(getGlobalIdTableNameWithSchema());
if (globalId instanceof ValueObjectId) {
final ValueObjectId valueObjectId = (ValueObjectId) globalId;
Optional<Long> ownerFk = findGlobalIdPk(valueObjectId.getOwnerId());
if (!ownerFk.isPresent()){
return Optional.empty();
}
query.where(GLOBAL_ID_FRAGMENT + " = :fragment ")
.withArgument("fragment", valueObjectId.getFragment())
.append("AND " + GLOBAL_ID_OWNER_ID_FK + " = :ownerFk ")
.withArgument("ownerFk", ownerFk.get());
}
else if (globalId instanceof InstanceId){
query
.where(GLOBAL_ID_LOCAL_ID + " = :localId ")
.withArgument("localId", jsonConverter.toJson(((InstanceId)globalId).getCdoId()))
.append("AND " + GLOBAL_ID_TYPE_NAME + " = :qualifiedName ")
.withArgument("qualifiedName", globalId.getTypeName());
}
else if (globalId instanceof UnboundedValueObjectId){
query
.where(GLOBAL_ID_TYPE_NAME + " = :qualifiedName ")
.withArgument("qualifiedName", globalId.getTypeName());
}
return queryForOptionalLong(query, polyJdbc);
}
private long insert(GlobalId globalId) {
InsertQuery query = polyJdbc.query()
.insert()
.into(getGlobalIdTableNameWithSchema());
if (globalId instanceof ValueObjectId) {
ValueObjectId valueObjectId = (ValueObjectId) globalId;
long ownerFk = getOrInsertId(valueObjectId.getOwnerId());
query.value(GLOBAL_ID_FRAGMENT, valueObjectId.getFragment())
.value(GLOBAL_ID_OWNER_ID_FK, ownerFk);
}
else if (globalId instanceof InstanceId) {
query.value(GLOBAL_ID_TYPE_NAME, globalId.getTypeName())
.value(GLOBAL_ID_LOCAL_ID, jsonConverter.toJson(((InstanceId)globalId).getCdoId()));
}
else if (globalId instanceof UnboundedValueObjectId) {
query.value(GLOBAL_ID_TYPE_NAME, globalId.getTypeName());
}
query.sequence(GLOBAL_ID_PK, getGlobalIdPkSeqWithSchema());
return polyJdbc.queryRunner().insert(query);
}
public void setJsonConverter(JsonConverter JSONConverter) {
this.jsonConverter = JSONConverter;
}
}