Commit f6e2907d authored by Luna Riegel's avatar Luna Riegel
Browse files

Refactor: Rework Database table structure

All CityObjects are now saved to a single
"features" table instead of per file tables.
parent 177f16ac
......@@ -15,16 +15,10 @@ public class CacheManager {
private CacheManager() {}
public static FeatureCache getFeatureCache(File file) {
if (file == null){
throw new IllegalArgumentException("File is null");
}
String filename = FilenameUtils.removeExtension(file.getName());
public static FeatureCache getFeatureCache() {
if (cache == null){
cache = new FeatureCache(filename);
} else {
cache.resetCache();
cache.changeFileName(filename);
cache = new FeatureCache();
}
return cache;
}
......
......@@ -13,7 +13,9 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.h2gis.functions.factory.H2GISDBFactory;
import org.h2gis.utilities.wrapper.ConnectionWrapper;
import org.h2gis.utilities.wrapper.DataSourceWrapper;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
......@@ -28,52 +30,36 @@ import java.util.Map;
public class DatabaseHandler {
private static final Logger logger = LogManager.getLogger(DatabaseHandler.class);
private static final String DB_NAME = "cd_db";
private final String fileName;
// TODO: Change handler back to using only a single featuretable
static{
// Creates the Database at initialization
private static final String DB_NAME = "/database/cd_db";
private DataSourceWrapper dataSource;
//TODO: Connection pooling
public DatabaseHandler(){
try {
// Cleanup hook Runtime.getRuntime().addShutdownHook(new Thread(() -> {}));
H2GISDBFactory.createSpatialDataBase(DB_NAME,
true, ";FILE_LOCK=SOCKET");
dataSource = new DataSourceWrapper(H2GISDBFactory.createDataSource(DB_NAME,
true, ";AUTO_SERVER=TRUE;MV_STORE=TRUE;CACHE_SIZE=65536;LOCK_TIMEOUT=10000"));
setupFeaturesTable();
} catch (Exception e) {
logger.fatal("FATAL ERROR: Creation of embedded database failed!");
logger.fatal(e.getMessage());
}
}
public DatabaseHandler(String fileName){
this.fileName = FilenameUtils.removeExtension(fileName).replaceAll(" [^a-zA-Z0-9]","");
setupFeaturesTable();
}
/**
* Sets up a new, empty embedded database. Will override an existing embedded database.
*/
public static void setupDatabase() {
}
//TODO: Refactor this to remove filename
public String getFileName() {
return fileName;
}
public void setupFeaturesTable() {
try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) {
try (PreparedStatement dropPs = con.prepareStatement(String.format("DROP TABLE IF EXISTS %s", fileName))) {
try (Connection con = H2GISDBFactory.openSpatialDataBase(DB_NAME)) {
try (PreparedStatement dropPs = con.prepareStatement("DROP TABLE IF EXISTS features")){
dropPs.executeUpdate();
if (logger.isDebugEnabled()) {
logger.debug("Dropped existing features table");
}
}
// TODO: Remove type and add a row with an atomic integer model id
// TODO: Refactor this to only use a single feature table
try (PreparedStatement createPs = con.prepareStatement(String.format("CREATE TABLE %s (gmlid VARCHAR(255)" +
" PRIMARY KEY, bbox GEOMETRY, data BLOB)", fileName))) {
try (PreparedStatement createPs = con.prepareStatement("CREATE TABLE features (gmlid VARCHAR(255)" +
" PRIMARY KEY, bbox GEOMETRY, data BLOB)")) {
createPs.executeUpdate();
if (logger.isDebugEnabled()) {
logger.debug("Created features table");
......@@ -92,8 +78,8 @@ public class DatabaseHandler {
* @param co the CityObject
*/
public void marshallCityObject(CityObject co) {
try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) {
try (PreparedStatement ps = con.prepareStatement(String.format("MERGE INTO %s VALUES (?, ?, ?)", fileName))) {
try (Connection con = H2GISDBFactory.openSpatialDataBase(DB_NAME)) {
try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?)")) {
ps.setString(1, co.getGmlId().toString());
String bbox = BoundingBox.of(co).to2DWkt();
ps.setString(2, bbox);
......@@ -120,8 +106,8 @@ public class DatabaseHandler {
public boolean tryMarshallCityObjectMap(Map<GmlId, CityObject> map) {
// Id String for error logging
String id = "";
try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) {
try (PreparedStatement ps = con.prepareStatement(String.format("MERGE INTO %s VALUES (?, ?, ?)", fileName))) {
try (Connection con = H2GISDBFactory.openSpatialDataBase(DB_NAME)) {
try (PreparedStatement ps = con.prepareStatement("MERGE INTO features VALUES (?, ?, ?)")) {
con.setAutoCommit(false);
for (Map.Entry<GmlId, CityObject> entry : map.entrySet()) {
CityObject co = entry.getValue();
......@@ -164,9 +150,9 @@ public class DatabaseHandler {
* @return the Feature, or null if no entry was found
*/
public CityObject unmarshallCityObject(GmlId id) {
try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) {
try (PreparedStatement ps = con.prepareStatement(String.format(
"SELECT gmlid, bbox, data FROM %s WHERE gmlid = ?", fileName))) {
try (Connection con = H2GISDBFactory.openSpatialDataBase(DB_NAME)) {
try (PreparedStatement ps = con.prepareStatement(
"SELECT gmlid, bbox, data FROM features WHERE gmlid = ?")) {
ps.setString(1, id.toString());
ResultSet rs = ps.executeQuery();
if (rs.next()) {
......@@ -187,7 +173,7 @@ public class DatabaseHandler {
}
} catch (SQLException e) {
logger.error("Loading of city object from embedded database failed!");
logger.debug(e.getMessage());
logger.error(e.getMessage());
}
return null;
......@@ -196,7 +182,7 @@ public class DatabaseHandler {
public List<CityObject> getAllCityObjects() {
List<CityObject> cos = new ArrayList<>();
try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) {
try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid, bbox, data FROM %s", fileName))) {
try (PreparedStatement ps = con.prepareStatement("SELECT gmlid, bbox, data FROM features")) {
ResultSet rs = ps.executeQuery();
cos.addAll(retrieveCityObjects(rs));
}
......@@ -210,8 +196,7 @@ public class DatabaseHandler {
public List<GmlId> getAllGmlIds() {
List<GmlId> cos = new ArrayList<>();
try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) {
try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid FROM %s", fileName))) {
ps.setString(1, fileName);
try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features")) {
ResultSet rs = ps.executeQuery();
while (rs.next()) {
cos.add(new GmlId(rs.getString("gmlid")));
......@@ -231,9 +216,9 @@ public class DatabaseHandler {
public List<GmlId> getBufferIntersectingObjects(GmlId gmlID, double bufferSize) {
List<GmlId> intersectingObjects = new ArrayList<>();
try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) {
try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid FROM %s AS tab1" +
" WHERE ST_INTERSECTS(ST_BUFFER(tab1.bbox,?), (SELECT bbox FROM %s WHERE gmlid = ?)) " +
"AND tab1.gmlid <> ?", fileName, fileName))) {
try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" +
" WHERE ST_INTERSECTS(ST_BUFFER(tab1.bbox,?), (SELECT bbox FROM features WHERE gmlid = ?)) " +
"AND tab1.gmlid <> ?")) {
ps.setDouble(1, bufferSize);
ps.setString(2, gmlID.toString());
ps.setString(3, gmlID.toString());
......@@ -257,9 +242,9 @@ public class DatabaseHandler {
List<GmlId> intersectingObjects = new ArrayList<>();
try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) {
try (PreparedStatement ps = con.prepareStatement(String.format("SELECT gmlid FROM %s AS tab1" +
" WHERE ST_INTERSECTS(tab1.bbox, (SELECT bbox FROM %s WHERE gmlid = ?)) AND tab1.gmlid <> ?",
fileName, fileName))) {
try (PreparedStatement ps = con.prepareStatement("SELECT gmlid FROM features AS tab1" +
" WHERE ST_INTERSECTS(tab1.bbox, (SELECT bbox FROM features WHERE gmlid = ?)) AND tab1.gmlid <> ?"))
{
ps.setString(1, id.toString());
ps.setString(2, id.toString());
ResultSet rs = ps.executeQuery();
......@@ -279,7 +264,7 @@ public class DatabaseHandler {
int count = -1;
try (Connection con = new ConnectionWrapper(H2GISDBFactory.openSpatialDataBase(DB_NAME))) {
try (PreparedStatement ps = con.prepareStatement(String.format("SELECT COUNT(gmlid) AS nr FROM %s", fileName))) {
try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(gmlid) AS nr FROM features")) {
ResultSet rs = ps.executeQuery();
if (rs.next()) {
count = rs.getInt("nr");
......
......@@ -30,8 +30,8 @@ public class FeatureCache implements CityObjectCache{
private AtomicInteger putsCounter = new AtomicInteger(0);
private boolean parsingFinished = false;
FeatureCache(String fileName) {
handler = new DatabaseHandler(fileName);
FeatureCache() {
handler = new DatabaseHandler();
handler.setupFeaturesTable();
cache = Caffeine.newBuilder().maximumSize(3000).removalListener((GmlId key, CityObject value, RemovalCause cause) -> {
if (value != null && cause.wasEvicted()) {
......@@ -79,23 +79,14 @@ public class FeatureCache implements CityObjectCache{
@Override
public CityObject get(GmlId id) {
CityObject result = cache.get(id);
if (result == null){
logger.trace("Cache-value missing for {}", id);
//Key has null value, try to restore from Database
result = handler.unmarshallCityObject(id);
if (result == null){
logger.error("CityObject with id {} is null in cache and database", id);
} else {
cache.put(id, result);
}
}
return result;
return cache.get(id);
}
public void changeFileName(String fileName){
handler = handlerList.computeIfAbsent(fileName, k -> {
DatabaseHandler handler1 = new DatabaseHandler(fileName);
DatabaseHandler handler1 = new DatabaseHandler();
handler1.setupFeaturesTable();
return handler1;
});
......@@ -131,8 +122,6 @@ public class FeatureCache implements CityObjectCache{
@Override
public List<CityObject> retrieveLibraryObject(String fileName) throws InvalidGmlFileException {
String prevHandler = handler.getFileName();
this.changeFileName(fileName);
int featureCount = getTotalFeatureCount();
if (featureCount != 1){
if (featureCount == 0){
......@@ -142,7 +131,6 @@ public class FeatureCache implements CityObjectCache{
}
}
List<CityObject> objects = handler.getAllCityObjects();
this.changeFileName(prevHandler);
return objects;
}
......
......@@ -68,7 +68,7 @@ public class CityDoctorModel {
private final CityObjectCache cache;
public CityDoctorModel(ParserConfiguration config, File file) {
this(config, file, CacheManager.getFeatureCache(file));
this(config, file, CacheManager.getFeatureCache());
}
public CityDoctorModel(ParserConfiguration config, File file, CityObjectCache cache) {
......
......@@ -37,7 +37,7 @@ public class DataBaseHandlerTest {
@Before
public void resetDatabase() {
try {
handler = new DatabaseHandler("test");
handler = new DatabaseHandler();
handler.setupFeaturesTable();
} catch (Exception e) {
fail(e.getMessage());
......@@ -242,7 +242,6 @@ public class DataBaseHandlerTest {
Building b = model.getBuildings().toList().get(0);
assertNotNull(b);
assertEquals(0, handler.getFeatureCount());
handler.marshallCityObject(b);
assertEquals(1, handler.getFeatureCount());
......
......@@ -13,7 +13,7 @@ public class FeatureCacheTest {
@Test
public void testPut(){
FeatureCache cache = new FeatureCache("test");
FeatureCache cache = new FeatureCache();
Building b = new Building();
b.setGmlObject(mock(AbstractBuilding.class));
b.setGmlId(new GmlId("test"));
......@@ -27,8 +27,8 @@ public class FeatureCacheTest {
@Test
public void testGet(){
FeatureCache cache = new FeatureCache("test");
DatabaseHandler handler = new DatabaseHandler("test");
FeatureCache cache = new FeatureCache();
DatabaseHandler handler = new DatabaseHandler();
Building b = new Building();
b.setGmlId(new GmlId("building"));
b.addGeometry(GeometryTestUtils.createDummyGeometry(GeometryType.SOLID, Lod.LOD0));
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment