From 1c73bae2e23242673e0a07cdbc2493700f134ceb Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 13 Feb 2022 23:34:25 -0800 Subject: New storage implementation --- .../java/org/traccar/database/DataManager.java | 265 ++++++++------------- 1 file changed, 103 insertions(+), 162 deletions(-) (limited to 'src/main/java/org/traccar/database/DataManager.java') diff --git a/src/main/java/org/traccar/database/DataManager.java b/src/main/java/org/traccar/database/DataManager.java index 199167419..303b8e033 100644 --- a/src/main/java/org/traccar/database/DataManager.java +++ b/src/main/java/org/traccar/database/DataManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,15 +41,20 @@ import org.traccar.model.Group; import org.traccar.model.Maintenance; import org.traccar.model.ManagedUser; import org.traccar.model.Notification; -import org.traccar.model.Order; import org.traccar.model.Permission; import org.traccar.model.Position; import org.traccar.model.Server; import org.traccar.model.Statistics; import org.traccar.model.User; +import org.traccar.storage.DatabaseStorage; import org.traccar.storage.QueryBuilder; -import org.traccar.storage.QueryExtended; -import org.traccar.storage.QueryIgnore; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Limit; +import org.traccar.storage.query.Order; +import org.traccar.storage.query.Request; import javax.sql.DataSource; import java.beans.Introspector; @@ -57,11 +62,10 @@ import java.io.File; import java.lang.reflect.Method; import java.net.URL; import java.sql.SQLException; -import java.util.Arrays; import java.util.Collection; import java.util.Date; -import java.util.HashSet; -import java.util.Set; +import java.util.LinkedList; +import java.util.List; public class DataManager { @@ -81,6 +85,8 @@ public class DataManager { return dataSource; } + private final Storage storage; + private boolean generateQueries; private final boolean forceLdap; @@ -92,6 +98,8 @@ public class DataManager { initDatabase(); initDatabaseSchema(); + + storage = new DatabaseStorage(dataSource); } private void initDatabase() throws Exception { @@ -134,58 +142,6 @@ public class DataManager { dataSource = new HikariDataSource(hikariConfig); } - public static String constructObjectQuery(String action, Class clazz, boolean extended) { - switch (action) { - case ACTION_INSERT: - case ACTION_UPDATE: - StringBuilder result = new StringBuilder(); - StringBuilder fields = new StringBuilder(); - StringBuilder values = new StringBuilder(); - - Set methods = new HashSet<>(Arrays.asList(clazz.getMethods())); - methods.removeAll(Arrays.asList(Object.class.getMethods())); - methods.removeAll(Arrays.asList(BaseModel.class.getMethods())); - for (Method method : methods) { - boolean skip; - if (extended) { - skip = !method.isAnnotationPresent(QueryExtended.class); - } else { - skip = method.isAnnotationPresent(QueryIgnore.class) - || method.isAnnotationPresent(QueryExtended.class) && !action.equals(ACTION_INSERT); - } - if (!skip && method.getName().startsWith("get") && method.getParameterTypes().length == 0) { - String name = Introspector.decapitalize(method.getName().substring(3)); - if (action.equals(ACTION_INSERT)) { - fields.append(name).append(", "); - values.append(":").append(name).append(", "); - } else { - fields.append(name).append(" = :").append(name).append(", "); - } - } - } - fields.setLength(fields.length() - 2); - if (action.equals(ACTION_INSERT)) { - values.setLength(values.length() - 2); - result.append("INSERT INTO ").append(getObjectsTableName(clazz)).append(" ("); - result.append(fields).append(") "); - result.append("VALUES (").append(values).append(")"); - } else { - result.append("UPDATE ").append(getObjectsTableName(clazz)).append(" SET "); - result.append(fields); - result.append(" WHERE id = :id"); - } - return result.toString(); - case ACTION_SELECT_ALL: - return "SELECT * FROM " + getObjectsTableName(clazz); - case ACTION_SELECT: - return "SELECT * FROM " + getObjectsTableName(clazz) + " WHERE id = :id"; - case ACTION_DELETE: - return "DELETE FROM " + getObjectsTableName(clazz) + " WHERE id = :id"; - default: - throw new IllegalArgumentException("Unknown action"); - } - } - public static String constructPermissionQuery(String action, Class owner, Class property) { switch (action) { case ACTION_SELECT_ALL: @@ -204,39 +160,6 @@ public class DataManager { } } - private String getQuery(String key) { - String query = config.getString(key); - if (query == null) { - LOGGER.info("Query not provided: " + key); - } - return query; - } - - public String getQuery(String action, Class clazz) { - return getQuery(action, clazz, false); - } - - public String getQuery(String action, Class clazz, boolean extended) { - String queryName; - if (action.equals(ACTION_SELECT_ALL)) { - queryName = "database.select" + clazz.getSimpleName() + "s"; - } else { - queryName = "database." + action.toLowerCase() + clazz.getSimpleName(); - if (extended) { - queryName += "Extended"; - } - } - String query = config.getString(queryName); - if (query == null) { - if (generateQueries) { - query = constructObjectQuery(action, clazz, extended); - } else { - LOGGER.info("Query not provided: " + queryName); - } - } - return query; - } - public String getQuery(String action, Class owner, Class property) { String queryName; switch (action) { @@ -302,10 +225,12 @@ public class DataManager { } } - public User login(String email, String password) throws SQLException { - User user = QueryBuilder.create(dataSource, getQuery("database.loginUser")) - .setString("email", email.trim()) - .executeQuerySingle(User.class); + public User login(String email, String password) throws StorageException { + User user = storage.getObject(User.class, new Request( + new Columns.Include("id", "login", "hashedPassword", "salt"), + new Condition.Or( + new Condition.Equals("email", "email", email.trim()), + new Condition.Equals("login", "email")))); LdapProvider ldapProvider = Context.getLdapProvider(); if (user != null) { if (ldapProvider != null && user.getLogin() != null && ldapProvider.login(user.getLogin(), password) @@ -322,57 +247,75 @@ public class DataManager { return null; } - public void updateDeviceStatus(Device device) throws SQLException { - QueryBuilder.create(dataSource, getQuery(ACTION_UPDATE, Device.class, true)) - .setObject(device) - .executeUpdate(); + public void updateUserPassword(User user) throws StorageException { + storage.updateObject(user, new Request( + new Columns.Include("hashedPassword", "salt"), + new Condition.Equals("id", "id"))); } - public Collection getPositions(long deviceId, Date from, Date to) throws SQLException { - return QueryBuilder.create(dataSource, getQuery("database.selectPositions")) - .setLong("deviceId", deviceId) - .setDate("from", from) - .setDate("to", to) - .executeQuery(Position.class); + public void updateDeviceStatus(Device device) throws StorageException { + storage.updateObject(device, new Request( + new Columns.Include("lastUpdate"), + new Condition.Equals("id", "id"))); } - public Position getPrecedingPosition(long deviceId, Date date) throws SQLException { - return QueryBuilder.create(dataSource, getQuery("database.selectPrecedingPosition")) - .setLong("deviceId", deviceId) - .setDate("time", date) - .executeQuerySingle(Position.class); + public Collection getPositions(long deviceId, Date from, Date to) throws StorageException { + return storage.getObjects(Position.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", "deviceId", deviceId), + new Condition.Between("fixTime", "from", from, "to", to)), + new Order("fixTime"))); } - public void updateLatestPosition(Position position) throws SQLException { - QueryBuilder.create(dataSource, getQuery("database.updateLatestPosition")) - .setDate("now", new Date()) - .setObject(position) - .executeUpdate(); + public Position getPrecedingPosition(long deviceId, Date date) throws StorageException { + return storage.getObject(Position.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", "deviceId", deviceId), + new Condition.Compare("fixTime", "<=", "time", date)), + new Order(true, "fixTime"), + new Limit(1))); } - public Collection getLatestPositions() throws SQLException { - return QueryBuilder.create(dataSource, getQuery("database.selectLatestPositions")) - .executeQuery(Position.class); + public void updateLatestPosition(Position position) throws StorageException { + Device device = new Device(); + device.setId(position.getDeviceId()); + device.setPositionId(position.getId()); + storage.updateObject(device, new Request( + new Columns.Include("positionId"), + new Condition.Equals("id", "id"))); + } + + public Collection getLatestPositions() throws StorageException { + List positions = new LinkedList<>(); + List devices = storage.getObjects(Device.class, new Request(new Columns.Include("positionId"))); + for (Device device : devices) { + positions.addAll(storage.getObjects(Position.class, new Request( + new Columns.All(), + new Condition.Equals("id", "id", device.getPositionId())))); + } + return positions; } - public Server getServer() throws SQLException { - return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, Server.class)) - .executeQuerySingle(Server.class); + public Server getServer() throws StorageException { + return storage.getObject(Server.class, new Request(new Columns.All())); } - public Collection getEvents(long deviceId, Date from, Date to) throws SQLException { - return QueryBuilder.create(dataSource, getQuery("database.selectEvents")) - .setLong("deviceId", deviceId) - .setDate("from", from) - .setDate("to", to) - .executeQuery(Event.class); + public Collection getEvents(long deviceId, Date from, Date to) throws StorageException { + return storage.getObjects(Event.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", "deviceId", deviceId), + new Condition.Between("eventTime", "from", from, "to", to)), + new Order("eventTime"))); } - public Collection getStatistics(Date from, Date to) throws SQLException { - return QueryBuilder.create(dataSource, getQuery("database.selectStatistics")) - .setDate("from", from) - .setDate("to", to) - .executeQuery(Statistics.class); + public Collection getStatistics(Date from, Date to) throws StorageException { + return storage.getObjects(Statistics.class, new Request( + new Columns.All(), + new Condition.Between("captureTime", "from", from, "to", to), + new Order("captureTime"))); } public static Class getClassByName(String name) throws ClassNotFoundException { @@ -400,7 +343,7 @@ public class DataManager { case "notification": return Notification.class; case "order": - return Order.class; + return org.traccar.model.Order.class; default: throw new ClassNotFoundException(); } @@ -412,51 +355,49 @@ public class DataManager { } public Collection getPermissions(Class owner, Class property) - throws SQLException, ClassNotFoundException { - return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, owner, property)) - .executePermissionsQuery(); + throws StorageException, ClassNotFoundException { + try { + return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, owner, property)) + .executePermissionsQuery(); + } catch (SQLException e) { + throw new StorageException(e); + } } public void linkObject(Class owner, long ownerId, Class property, long propertyId, boolean link) - throws SQLException { + throws StorageException { + try { QueryBuilder.create(dataSource, getQuery(link ? ACTION_INSERT : ACTION_DELETE, owner, property)) .setLong(makeNameId(owner), ownerId) .setLong(makeNameId(property), propertyId) .executeUpdate(); + } catch (SQLException e) { + throw new StorageException(e); + } } - public T getObject(Class clazz, long entityId) throws SQLException { - return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT, clazz)) - .setLong("id", entityId) - .executeQuerySingle(clazz); + public T getObject(Class clazz, long entityId) throws StorageException { + return storage.getObject(clazz, new Request( + new Columns.All(), + new Condition.Equals("id", "id", entityId))); } - public Collection getObjects(Class clazz) throws SQLException { - return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, clazz)) - .executeQuery(clazz); + public Collection getObjects(Class clazz) throws StorageException { + return storage.getObjects(clazz, new Request(new Columns.All())); } - public void addObject(BaseModel entity) throws SQLException { - entity.setId(QueryBuilder.create(dataSource, getQuery(ACTION_INSERT, entity.getClass()), true) - .setObject(entity) - .executeUpdate()); + public void addObject(BaseModel entity) throws StorageException { + entity.setId(storage.addObject(entity, new Request(new Columns.Exclude("id")))); } - public void updateObject(BaseModel entity) throws SQLException { - QueryBuilder.create(dataSource, getQuery(ACTION_UPDATE, entity.getClass())) - .setObject(entity) - .executeUpdate(); - if (entity instanceof User && ((User) entity).getHashedPassword() != null) { - QueryBuilder.create(dataSource, getQuery(ACTION_UPDATE, User.class, true)) - .setObject(entity) - .executeUpdate(); - } + public void updateObject(BaseModel entity) throws StorageException { + storage.updateObject(entity, new Request( + new Columns.Exclude("id"), + new Condition.Equals("id", "id"))); } - public void removeObject(Class clazz, long entityId) throws SQLException { - QueryBuilder.create(dataSource, getQuery(ACTION_DELETE, clazz)) - .setLong("id", entityId) - .executeUpdate(); + public void removeObject(Class clazz, long entityId) throws StorageException { + storage.removeObject(clazz, new Request(new Condition.Equals("id", "id", entityId))); } } -- cgit v1.2.3