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