mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
server: database improves:
- fixed broken database in some use cases (example: AI and choose name dialog, related to #11285); - added docs and debug tools for sql queries, caches and memory analyse (see DebugUtil); - refactor code to use shared settings; - deleted outdated and un-used code (db logs, stats, etc);
This commit is contained in:
parent
c448612c97
commit
bf3f26ccc1
18 changed files with 376 additions and 394 deletions
|
|
@ -1774,7 +1774,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
|
||||
private void doClientShutdownAndExit() {
|
||||
tablesPane.cleanUp();
|
||||
CardRepository.instance.closeDB();
|
||||
CardRepository.instance.closeDB(true);
|
||||
Plugins.instance.shutdown();
|
||||
dispose();
|
||||
System.exit(0);
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ import com.j256.ormlite.dao.DaoManager;
|
|||
import com.j256.ormlite.jdbc.JdbcConnectionSource;
|
||||
import com.j256.ormlite.support.ConnectionSource;
|
||||
import com.j256.ormlite.table.TableUtils;
|
||||
import mage.cards.repository.DatabaseUtils;
|
||||
import mage.db.model.Feedback;
|
||||
import mage.db.model.Log;
|
||||
import mage.utils.properties.PropertiesUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.SQLException;
|
||||
|
|
@ -21,20 +20,15 @@ public enum EntityManager {
|
|||
|
||||
instance;
|
||||
|
||||
private Dao<Log, Object> logDao;
|
||||
private Dao<Feedback, Object> feedbackDao;
|
||||
|
||||
private EntityManager() {
|
||||
EntityManager() {
|
||||
File file = new File("db");
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
try {
|
||||
ConnectionSource logConnectionSource = new JdbcConnectionSource(PropertiesUtil.getDBLogUrl());
|
||||
TableUtils.createTableIfNotExists(logConnectionSource, Log.class);
|
||||
logDao = DaoManager.createDao(logConnectionSource, Log.class);
|
||||
|
||||
ConnectionSource feedbackConnectionSource = new JdbcConnectionSource(PropertiesUtil.getDBFeedbackUrl());
|
||||
ConnectionSource feedbackConnectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_FEEDBACK, false));
|
||||
TableUtils.createTableIfNotExists(feedbackConnectionSource, Feedback.class);
|
||||
feedbackDao = DaoManager.createDao(feedbackConnectionSource, Feedback.class);
|
||||
} catch (SQLException ex) {
|
||||
|
|
@ -42,22 +36,6 @@ public enum EntityManager {
|
|||
}
|
||||
}
|
||||
|
||||
public void insertLog(String key, java.util.Date date, String... args) throws SQLException {
|
||||
Log logEntity = new Log(key, date);
|
||||
logEntity.setArguments(args);
|
||||
logDao.create(logEntity);
|
||||
}
|
||||
|
||||
public List<Log> getAllLogs() {
|
||||
List<Log> logs = new ArrayList<>();
|
||||
try {
|
||||
logs = logDao.queryForAll();
|
||||
} catch (SQLException ex) {
|
||||
}
|
||||
|
||||
return logs;
|
||||
}
|
||||
|
||||
public void insertFeedback(String username, String title, String type, String message, String email, String host, java.util.Date created) throws SQLException {
|
||||
Feedback feedback = new Feedback(username, title, type, message, email, host, created, "new");
|
||||
feedbackDao.create(feedback);
|
||||
|
|
|
|||
|
|
@ -12,26 +12,7 @@ import java.util.List;
|
|||
*/
|
||||
public final class EntityManagerTest {
|
||||
|
||||
private static DateFormat timeFormatter = SimpleDateFormat.getTimeInstance(SimpleDateFormat.FULL);
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
List<Log> logs = EntityManager.instance.getAllLogs();
|
||||
System.out.println("logs found: " + logs.size());
|
||||
for (Log log : logs) {
|
||||
System.out.println(" key=" + log.getKey());
|
||||
System.out.println(" date=" + timeFormatter.format(log.getCreatedDate()));
|
||||
System.out.print(" arguments=[ ");
|
||||
if (log.getArguments() != null) {
|
||||
for (String argument : log.getArguments()) {
|
||||
System.out.print("arg=" + argument + ' ');
|
||||
}
|
||||
}
|
||||
System.out.println("]");
|
||||
System.out.println(" --------------");
|
||||
}
|
||||
|
||||
System.out.println("********************************");
|
||||
|
||||
List<Feedback> feedbackList = EntityManager.instance.getAllFeedbacks();
|
||||
System.out.println("feedbacks found: " + feedbackList.size());
|
||||
int count = 1;
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
package mage.db;
|
||||
|
||||
import mage.db.model.Log;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author noxx
|
||||
*/
|
||||
public final class Statistics {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
List<Log> logs = EntityManager.instance.getAllLogs();
|
||||
System.out.println("logs found: " + logs.size());
|
||||
|
||||
Map<String, Integer> nicknames = displayCommonNumbers(logs);
|
||||
List<Integer> games = displayTop3(nicknames);
|
||||
displayPlayedOnlyOnce(games);
|
||||
|
||||
System.out.println("Done");
|
||||
}
|
||||
|
||||
private static void displayPlayedOnlyOnce(List<Integer> games) {
|
||||
Integer oneGame = 0;
|
||||
for (Integer numberOfGames : games) {
|
||||
if (numberOfGames == 1) {
|
||||
oneGame++;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Number of players played only one game: " + oneGame);
|
||||
}
|
||||
|
||||
private static List<Integer> displayTop3(Map<String, Integer> nicknames) {
|
||||
Collection<Integer> values = nicknames.values();
|
||||
List<Integer> games = new ArrayList<>();
|
||||
games.addAll(values);
|
||||
Collections.sort(games, new Comparator<Integer>() {
|
||||
@Override
|
||||
public int compare(Integer i1, Integer i2) {
|
||||
return i2.compareTo(i1);
|
||||
}
|
||||
});
|
||||
|
||||
// Top-3
|
||||
List<Integer> numbersToFind = new ArrayList<>();
|
||||
for (Integer numberOfGames : games) {
|
||||
numbersToFind.add(numberOfGames);
|
||||
if (numbersToFind.size() == 3) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Map<Integer, String> players = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Integer> entry : nicknames.entrySet()) {
|
||||
if (check(numbersToFind, entry.getValue())) {
|
||||
players.put(entry.getValue(), entry.getKey());
|
||||
}
|
||||
if (players.size() == 3) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Top-3");
|
||||
for (Map.Entry<Integer, String> entry : players.entrySet()) {
|
||||
System.out.println(" " + entry.getValue() + ": " + entry.getKey());
|
||||
}
|
||||
return games;
|
||||
}
|
||||
|
||||
private static Map<String, Integer> displayCommonNumbers(List<Log> logs) {
|
||||
int count = 0;
|
||||
Map<String, Integer> nicknames = new HashMap<>();
|
||||
for (Log log : logs) {
|
||||
if (log.getKey().equals("gameStarted")) {
|
||||
if (log.getArguments() != null) {
|
||||
int index = 0;
|
||||
for (String argument : log.getArguments()) {
|
||||
if (index > 0) {
|
||||
inc(nicknames, argument);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("********************************");
|
||||
System.out.println("Games played: " + count);
|
||||
System.out.println("Number of players: " + nicknames.size());
|
||||
return nicknames;
|
||||
}
|
||||
|
||||
public static void inc(Map<String, Integer> map, String player) {
|
||||
if (map.containsKey(player)) {
|
||||
Integer count = map.get(player);
|
||||
count++;
|
||||
map.put(player, count);
|
||||
} else {
|
||||
map.put(player, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean check(List<Integer> numbers, Integer value) {
|
||||
for (Integer number : numbers) {
|
||||
if (number.equals(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
package mage.utils.properties;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author noxx
|
||||
*/
|
||||
public final class PropertiesUtil {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(PropertiesUtil.class);
|
||||
|
||||
private static final String LOG_JDBC_URL = "jdbc:h2:file:./db/mage.h2;AUTO_SERVER=TRUE";
|
||||
private static final String FEEDBACK_JDBC_URL = "jdbc:h2:file:./db/feedback.h2;AUTO_SERVER=TRUE";
|
||||
|
||||
private static Properties properties = new Properties();
|
||||
|
||||
static {
|
||||
try (InputStream in = PropertiesUtil.class.getResourceAsStream("/xmage.properties")) {
|
||||
if(in != null) {
|
||||
properties.load(in);
|
||||
} else {
|
||||
logger.warn("No xmage.properties were found");
|
||||
}
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
logger.warn("No xmage.properties were found on classpath");
|
||||
} catch (IOException e) {
|
||||
logger.error("Couldn't load properties");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide constructor
|
||||
*/
|
||||
private PropertiesUtil() {
|
||||
|
||||
}
|
||||
|
||||
public static String getDBLogUrl () {
|
||||
String url = properties.getProperty(PropertyKeys.KEY_DB_LOG_URL, LOG_JDBC_URL);
|
||||
if (url != null) {
|
||||
return url.trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getDBFeedbackUrl () {
|
||||
String url = properties.getProperty(PropertyKeys.KEY_DB_FEEDBACK_URL, FEEDBACK_JDBC_URL);
|
||||
if (url != null) {
|
||||
return url.trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package mage.utils.properties;
|
||||
|
||||
/**
|
||||
* @author noxx
|
||||
*/
|
||||
public final class PropertyKeys {
|
||||
|
||||
public static final String KEY_DB_LOG_URL = "db.log.url";
|
||||
public static final String KEY_DB_FEEDBACK_URL = "db.feedback.url";
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import com.j256.ormlite.support.ConnectionSource;
|
|||
import com.j256.ormlite.support.DatabaseConnection;
|
||||
import com.j256.ormlite.table.TableUtils;
|
||||
import mage.cards.repository.CardRepository;
|
||||
import mage.cards.repository.DatabaseUtils;
|
||||
import mage.cards.repository.RepositoryUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.shiro.crypto.RandomNumberGenerator;
|
||||
|
|
@ -24,7 +25,6 @@ import java.util.List;
|
|||
|
||||
public class AuthorizedUserRepository {
|
||||
|
||||
private static final String JDBC_URL = "jdbc:h2:file:./db/authorized_user.h2;AUTO_SERVER=TRUE";
|
||||
private static final String VERSION_ENTITY_NAME = "authorized_user";
|
||||
// raise this if db structure was changed
|
||||
private static final long DB_VERSION = 2;
|
||||
|
|
@ -32,10 +32,10 @@ public class AuthorizedUserRepository {
|
|||
|
||||
private static final AuthorizedUserRepository instance;
|
||||
static {
|
||||
instance = new AuthorizedUserRepository(JDBC_URL);
|
||||
instance = new AuthorizedUserRepository(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_USERS, false));
|
||||
}
|
||||
|
||||
private Dao<AuthorizedUser, Object> dao;
|
||||
private Dao<AuthorizedUser, Object> usersDao;
|
||||
|
||||
public AuthorizedUserRepository(String connectionString) {
|
||||
File file = new File("db");
|
||||
|
|
@ -45,7 +45,7 @@ public class AuthorizedUserRepository {
|
|||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(connectionString);
|
||||
TableUtils.createTableIfNotExists(connectionSource, AuthorizedUser.class);
|
||||
dao = DaoManager.createDao(connectionSource, AuthorizedUser.class);
|
||||
usersDao = DaoManager.createDao(connectionSource, AuthorizedUser.class);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(AuthorizedUserRepository.class).error("Error creating / assigning authorized_user repository - ", ex);
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ public class AuthorizedUserRepository {
|
|||
try {
|
||||
Hash hash = new SimpleHash(Sha256Hash.ALGORITHM_NAME, password, rng.nextBytes(), 1024);
|
||||
AuthorizedUser user = new AuthorizedUser(userName, hash, email);
|
||||
dao.create(user);
|
||||
usersDao.create(user);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(AuthorizedUserRepository.class).error("Error adding a user to DB - ", ex);
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ public class AuthorizedUserRepository {
|
|||
|
||||
public void remove(final String userName) {
|
||||
try {
|
||||
DeleteBuilder<AuthorizedUser, Object> db = dao.deleteBuilder();
|
||||
DeleteBuilder<AuthorizedUser, Object> db = usersDao.deleteBuilder();
|
||||
db.where().eq("name", new SelectArg(userName));
|
||||
db.delete();
|
||||
} catch (SQLException ex) {
|
||||
|
|
@ -77,9 +77,9 @@ public class AuthorizedUserRepository {
|
|||
|
||||
public AuthorizedUser getByName(String userName) {
|
||||
try {
|
||||
QueryBuilder<AuthorizedUser, Object> qb = dao.queryBuilder();
|
||||
QueryBuilder<AuthorizedUser, Object> qb = usersDao.queryBuilder();
|
||||
qb.where().eq("name", new SelectArg(userName));
|
||||
List<AuthorizedUser> results = dao.query(qb.prepare());
|
||||
List<AuthorizedUser> results = usersDao.query(qb.prepare());
|
||||
if (results.size() == 1) {
|
||||
return results.get(0);
|
||||
}
|
||||
|
|
@ -92,7 +92,7 @@ public class AuthorizedUserRepository {
|
|||
|
||||
public void update(AuthorizedUser authorizedUser) {
|
||||
try {
|
||||
dao.update(authorizedUser);
|
||||
usersDao.update(authorizedUser);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(AuthorizedUserRepository.class).error("Error updating authorized_user", ex);
|
||||
}
|
||||
|
|
@ -100,9 +100,9 @@ public class AuthorizedUserRepository {
|
|||
|
||||
public AuthorizedUser getByEmail(String userName) {
|
||||
try {
|
||||
QueryBuilder<AuthorizedUser, Object> qb = dao.queryBuilder();
|
||||
QueryBuilder<AuthorizedUser, Object> qb = usersDao.queryBuilder();
|
||||
qb.where().eq("email", new SelectArg(userName));
|
||||
List<AuthorizedUser> results = dao.query(qb.prepare());
|
||||
List<AuthorizedUser> results = usersDao.query(qb.prepare());
|
||||
if (results.size() == 1) {
|
||||
return results.get(0);
|
||||
}
|
||||
|
|
@ -115,9 +115,10 @@ public class AuthorizedUserRepository {
|
|||
|
||||
public void closeDB() {
|
||||
try {
|
||||
if (dao != null && dao.getConnectionSource() != null) {
|
||||
DatabaseConnection conn = dao.getConnectionSource().getReadWriteConnection(dao.getTableName());
|
||||
conn.executeStatement("shutdown compact", 0);
|
||||
if (usersDao != null && usersDao.getConnectionSource() != null) {
|
||||
DatabaseConnection conn = usersDao.getConnectionSource().getReadWriteConnection(usersDao.getTableName());
|
||||
conn.executeStatement("SHUTDOWN IMMEDIATELY", 0);
|
||||
usersDao.getConnectionSource().releaseConnection(conn);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(AuthorizedUserRepository.class).error("Error closing authorized_user repository - ", ex);
|
||||
|
|
@ -126,7 +127,7 @@ public class AuthorizedUserRepository {
|
|||
|
||||
public long getDBVersionFromDB() {
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_USERS, false));
|
||||
return RepositoryUtil.getDatabaseVersion(connectionSource, VERSION_ENTITY_NAME);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CardRepository.class).error("Error getting DB version from DB - ", ex);
|
||||
|
|
@ -145,11 +146,11 @@ public class AuthorizedUserRepository {
|
|||
private boolean migrateFrom1To2() {
|
||||
try {
|
||||
Logger.getLogger(AuthorizedUserRepository.class).info("Starting " + VERSION_ENTITY_NAME + " DB migration from version 1 to version 2");
|
||||
dao.executeRaw("ALTER TABLE authorized_user ADD COLUMN active BOOLEAN DEFAULT true;");
|
||||
dao.executeRaw("ALTER TABLE authorized_user ADD COLUMN lockedUntil DATETIME;");
|
||||
dao.executeRaw("ALTER TABLE authorized_user ADD COLUMN chatLockedUntil DATETIME;");
|
||||
dao.executeRaw("ALTER TABLE authorized_user ADD COLUMN lastConnection DATETIME;");
|
||||
RepositoryUtil.updateVersion(dao.getConnectionSource(), VERSION_ENTITY_NAME, DB_VERSION);
|
||||
usersDao.executeRaw("ALTER TABLE authorized_user ADD COLUMN active BOOLEAN DEFAULT true;");
|
||||
usersDao.executeRaw("ALTER TABLE authorized_user ADD COLUMN lockedUntil DATETIME;");
|
||||
usersDao.executeRaw("ALTER TABLE authorized_user ADD COLUMN chatLockedUntil DATETIME;");
|
||||
usersDao.executeRaw("ALTER TABLE authorized_user ADD COLUMN lastConnection DATETIME;");
|
||||
RepositoryUtil.updateVersion(usersDao.getConnectionSource(), VERSION_ENTITY_NAME, DB_VERSION);
|
||||
Logger.getLogger(AuthorizedUserRepository.class).info("Migration finished.");
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.j256.ormlite.stmt.SelectArg;
|
|||
import com.j256.ormlite.support.ConnectionSource;
|
||||
import com.j256.ormlite.support.DatabaseConnection;
|
||||
import com.j256.ormlite.table.TableUtils;
|
||||
import mage.cards.repository.DatabaseUtils;
|
||||
import mage.cards.repository.RepositoryUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
|
@ -20,12 +21,11 @@ public enum TableRecordRepository {
|
|||
|
||||
instance;
|
||||
|
||||
private static final String JDBC_URL = "jdbc:sqlite:./db/table_record.db";
|
||||
private static final String VERSION_ENTITY_NAME = "table_record";
|
||||
// raise this if db structure was changed
|
||||
private static final long DB_VERSION = 0;
|
||||
|
||||
private Dao<TableRecord, Object> dao;
|
||||
private Dao<TableRecord, Object> recordsDao;
|
||||
|
||||
TableRecordRepository() {
|
||||
File file = new File("db");
|
||||
|
|
@ -33,7 +33,7 @@ public enum TableRecordRepository {
|
|||
file.mkdirs();
|
||||
}
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareSqliteConnection(DatabaseUtils.DB_NAME_RECORDS));
|
||||
boolean obsolete = RepositoryUtil.isDatabaseObsolete(connectionSource, VERSION_ENTITY_NAME, DB_VERSION);
|
||||
|
||||
if (obsolete) {
|
||||
|
|
@ -41,7 +41,7 @@ public enum TableRecordRepository {
|
|||
}
|
||||
|
||||
TableUtils.createTableIfNotExists(connectionSource, TableRecord.class);
|
||||
dao = DaoManager.createDao(connectionSource, TableRecord.class);
|
||||
recordsDao = DaoManager.createDao(connectionSource, TableRecord.class);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(TableRecordRepository.class).error("Error creating table_record repository - ", ex);
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ public enum TableRecordRepository {
|
|||
|
||||
public void add(TableRecord tableHistory) {
|
||||
try {
|
||||
dao.create(tableHistory);
|
||||
recordsDao.create(tableHistory);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(TableRecordRepository.class).error("Error adding a table_record to DB - ", ex);
|
||||
}
|
||||
|
|
@ -57,10 +57,10 @@ public enum TableRecordRepository {
|
|||
|
||||
public List<TableRecord> getAfter(long endTimeMs) {
|
||||
try {
|
||||
QueryBuilder<TableRecord, Object> qb = dao.queryBuilder();
|
||||
QueryBuilder<TableRecord, Object> qb = recordsDao.queryBuilder();
|
||||
qb.where().gt("endTimeMs", new SelectArg(endTimeMs));
|
||||
qb.orderBy("endTimeMs", true);
|
||||
return dao.query(qb.prepare());
|
||||
return recordsDao.query(qb.prepare());
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(TableRecordRepository.class).error("Error getting table_records from DB - ", ex);
|
||||
}
|
||||
|
|
@ -69,9 +69,10 @@ public enum TableRecordRepository {
|
|||
|
||||
public void closeDB() {
|
||||
try {
|
||||
if (dao != null && dao.getConnectionSource() != null) {
|
||||
DatabaseConnection conn = dao.getConnectionSource().getReadWriteConnection(dao.getTableName());
|
||||
conn.executeStatement("shutdown compact", 0);
|
||||
if (recordsDao != null && recordsDao.getConnectionSource() != null) {
|
||||
DatabaseConnection conn = recordsDao.getConnectionSource().getReadWriteConnection(recordsDao.getTableName());
|
||||
conn.executeStatement("SHUTDOWN IMMEDIATELY", 0);
|
||||
recordsDao.getConnectionSource().releaseConnection(conn);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(TableRecordRepository.class).error("Error closing table_record repository - ", ex);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.j256.ormlite.stmt.SelectArg;
|
|||
import com.j256.ormlite.support.ConnectionSource;
|
||||
import com.j256.ormlite.support.DatabaseConnection;
|
||||
import com.j256.ormlite.table.TableUtils;
|
||||
import mage.cards.repository.DatabaseUtils;
|
||||
import mage.cards.repository.RepositoryUtil;
|
||||
import mage.game.result.ResultProtos;
|
||||
import mage.server.rating.GlickoRating;
|
||||
|
|
@ -22,12 +23,11 @@ public enum UserStatsRepository {
|
|||
|
||||
instance;
|
||||
|
||||
private static final String JDBC_URL = "jdbc:sqlite:./db/user_stats.db";
|
||||
private static final String VERSION_ENTITY_NAME = "user_stats";
|
||||
// raise this if db structure was changed
|
||||
private static final long DB_VERSION = 0;
|
||||
|
||||
private Dao<UserStats, Object> dao;
|
||||
private Dao<UserStats, Object> statsDao;
|
||||
|
||||
UserStatsRepository() {
|
||||
File file = new File("db");
|
||||
|
|
@ -35,7 +35,7 @@ public enum UserStatsRepository {
|
|||
file.mkdirs();
|
||||
}
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareSqliteConnection(DatabaseUtils.DB_NAME_STATS));
|
||||
boolean obsolete = RepositoryUtil.isDatabaseObsolete(connectionSource, VERSION_ENTITY_NAME, DB_VERSION);
|
||||
|
||||
if (obsolete) {
|
||||
|
|
@ -43,7 +43,7 @@ public enum UserStatsRepository {
|
|||
}
|
||||
|
||||
TableUtils.createTableIfNotExists(connectionSource, UserStats.class);
|
||||
dao = DaoManager.createDao(connectionSource, UserStats.class);
|
||||
statsDao = DaoManager.createDao(connectionSource, UserStats.class);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error creating user_stats repository - ", ex);
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ public enum UserStatsRepository {
|
|||
|
||||
public void add(UserStats userStats) {
|
||||
try {
|
||||
dao.create(userStats);
|
||||
statsDao.create(userStats);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error adding a user_stats to DB - ", ex);
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ public enum UserStatsRepository {
|
|||
|
||||
public void update(UserStats userStats) {
|
||||
try {
|
||||
dao.update(userStats);
|
||||
statsDao.update(userStats);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error updating a user_stats in DB - ", ex);
|
||||
}
|
||||
|
|
@ -67,9 +67,9 @@ public enum UserStatsRepository {
|
|||
|
||||
public UserStats getUser(String userName) {
|
||||
try {
|
||||
QueryBuilder<UserStats, Object> qb = dao.queryBuilder();
|
||||
QueryBuilder<UserStats, Object> qb = statsDao.queryBuilder();
|
||||
qb.limit(1L).where().eq("userName", new SelectArg(userName));
|
||||
List<UserStats> users = dao.query(qb.prepare());
|
||||
List<UserStats> users = statsDao.query(qb.prepare());
|
||||
if (!users.isEmpty()) {
|
||||
return users.get(0);
|
||||
}
|
||||
|
|
@ -81,8 +81,8 @@ public enum UserStatsRepository {
|
|||
|
||||
public List<UserStats> getAllUsers() {
|
||||
try {
|
||||
QueryBuilder<UserStats, Object> qb = dao.queryBuilder();
|
||||
return dao.query(qb.prepare());
|
||||
QueryBuilder<UserStats, Object> qb = statsDao.queryBuilder();
|
||||
return statsDao.query(qb.prepare());
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error getting all users from DB - ", ex);
|
||||
}
|
||||
|
|
@ -91,9 +91,9 @@ public enum UserStatsRepository {
|
|||
|
||||
public long getLatestEndTimeMs() {
|
||||
try {
|
||||
QueryBuilder<UserStats, Object> qb = dao.queryBuilder();
|
||||
QueryBuilder<UserStats, Object> qb = statsDao.queryBuilder();
|
||||
qb.orderBy("endTimeMs", false).limit(1L);
|
||||
List<UserStats> users = dao.query(qb.prepare());
|
||||
List<UserStats> users = statsDao.query(qb.prepare());
|
||||
if (!users.isEmpty()) {
|
||||
return users.get(0).getEndTimeMs();
|
||||
}
|
||||
|
|
@ -366,9 +366,10 @@ public enum UserStatsRepository {
|
|||
|
||||
public void closeDB() {
|
||||
try {
|
||||
if (dao != null && dao.getConnectionSource() != null) {
|
||||
DatabaseConnection conn = dao.getConnectionSource().getReadWriteConnection(dao.getTableName());
|
||||
conn.executeStatement("shutdown compact", 0);
|
||||
if (statsDao != null && statsDao.getConnectionSource() != null) {
|
||||
DatabaseConnection conn = statsDao.getConnectionSource().getReadWriteConnection(statsDao.getTableName());
|
||||
conn.executeStatement("SHUTDOWN IMMEDIATELY", 0);
|
||||
statsDao.getConnectionSource().releaseConnection(conn);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(UserStatsRepository.class).error("Error closing user_stats repository - ", ex);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public final class BrainPry extends CardImpl {
|
|||
public BrainPry(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}");
|
||||
|
||||
//Name a nonland card. Target player reveals their hand. That player discards a card with that name. If they can't, you draw a card.
|
||||
// Name a nonland card. Target player reveals their hand. That player discards a card with that name. If they can't, you draw a card.
|
||||
this.getSpellAbility().addEffect((new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME)));
|
||||
this.getSpellAbility().addTarget(new TargetPlayer());
|
||||
this.getSpellAbility().addEffect(new BrainPryEffect());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
package org.mage.test.serverside;
|
||||
|
||||
import mage.cards.repository.CardRepository;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class DatabaseBigQueryPerformanceTest extends CardTestPlayerBaseWithAIHelps {
|
||||
|
||||
@Test
|
||||
public void test_GetLands_SQL() {
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
Assert.assertTrue("must load getNames", CardRepository.instance.getNames().size() > 1000);
|
||||
Assert.assertTrue("must load getNonLandNames", CardRepository.instance.getNonLandNames().size() > 1000);
|
||||
Assert.assertTrue("must load getArtifactNames", CardRepository.instance.getArtifactNames().size() > 1000);
|
||||
Assert.assertTrue("must load getCreatureNames", CardRepository.instance.getCreatureNames().size() > 1000);
|
||||
Assert.assertTrue("must load getNonArtifactAndNonLandNames", CardRepository.instance.getNonArtifactAndNonLandNames().size() > 1000);
|
||||
Assert.assertTrue("must load getNonLandAndNonCreatureNames", CardRepository.instance.getNonLandAndNonCreatureNames().size() > 1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_GetLands_RealGame_Manual() {
|
||||
// Name a nonland card. Target player reveals their hand. That player discards a card with that name.
|
||||
// If they can't, you draw a card.
|
||||
addCard(Zone.HAND, playerA, "Brain Pry", 1); // {1}{B}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Brain Pry", playerA);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
setChoice(playerA, "Balduvian Bears"); // name to choose
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_GetLands_RealGame_AI() {
|
||||
// possible bug: game simulations can call big queries multiple times and overflow database cache to crash it
|
||||
// how-to fix: increase CACHE_SIZE in DatabaseUtils (require 150 000 kb on 2024)
|
||||
int cardsAmount = 5;
|
||||
|
||||
// Name a nonland card. Target player reveals their hand. That player discards a card with that name.
|
||||
// If they can't, you draw a card.
|
||||
addCard(Zone.HAND, playerA, "Brain Pry", cardsAmount); // {1}{B}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2 * cardsAmount);
|
||||
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
}
|
||||
}
|
||||
|
|
@ -19,8 +19,6 @@ import java.nio.file.Paths;
|
|||
*/
|
||||
public class DatabaseCompatibleTest {
|
||||
|
||||
private final String JDBC_URL = "jdbc:h2:file:%s;AUTO_SERVER=TRUE";
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||
|
||||
|
|
@ -38,9 +36,8 @@ public class DatabaseCompatibleTest {
|
|||
);
|
||||
Assert.assertTrue(Files.exists(Paths.get(dbFullFileName)));
|
||||
|
||||
AuthorizedUserRepository dbUsers = new AuthorizedUserRepository(
|
||||
String.format(JDBC_URL, dbFullName)
|
||||
);
|
||||
String connectionString = String.format("jdbc:h2:file:%s;AUTO_SERVER=TRUE", dbFullName);
|
||||
AuthorizedUserRepository dbUsers = new AuthorizedUserRepository(connectionString);
|
||||
|
||||
// search
|
||||
Assert.assertNotNull(dbUsers.getByName("user1"));
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package mage.cards.repository;
|
|||
|
||||
import com.j256.ormlite.dao.Dao;
|
||||
import com.j256.ormlite.dao.DaoManager;
|
||||
import com.j256.ormlite.dao.GenericRawResults;
|
||||
import com.j256.ormlite.jdbc.JdbcConnectionSource;
|
||||
import com.j256.ormlite.stmt.QueryBuilder;
|
||||
import com.j256.ormlite.stmt.SelectArg;
|
||||
|
|
@ -19,6 +20,7 @@ import java.io.File;
|
|||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author North, JayDi85
|
||||
|
|
@ -31,16 +33,15 @@ public enum CardRepository {
|
|||
|
||||
// fixes limit for out of memory problems
|
||||
private static final AtomicInteger databaseFixes = new AtomicInteger();
|
||||
private static final int MAX_DATABASE_FIXES = 3;
|
||||
|
||||
private static final String JDBC_URL = "jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE;IGNORECASE=TRUE";
|
||||
private static final int MAX_DATABASE_FIXES = 10;
|
||||
|
||||
// TODO: delete db version from cards and expansions due un-used (cause dbs re-created on each update now)
|
||||
private static final String VERSION_ENTITY_NAME = "card";
|
||||
// raise this if db structure was changed
|
||||
private static final long CARD_DB_VERSION = 54;
|
||||
// raise this if new cards were added to the server
|
||||
private static final long CARD_CONTENT_VERSION = 241;
|
||||
private Dao<CardInfo, Object> cardDao;
|
||||
private Set<String> classNames;
|
||||
private static final long CARD_DB_VERSION = 54; // raise this if db structure was changed
|
||||
private static final long CARD_CONTENT_VERSION = 241; // raise this if new cards were added to the server
|
||||
|
||||
private Dao<CardInfo, Object> cardsDao;
|
||||
|
||||
// sets with exclusively snow basics
|
||||
public static final Set<String> snowLandSetCodes = new HashSet<>(Arrays.asList(
|
||||
|
|
@ -55,7 +56,7 @@ public enum CardRepository {
|
|||
file.mkdirs();
|
||||
}
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_CARDS, true));
|
||||
|
||||
boolean isObsolete = RepositoryUtil.isDatabaseObsolete(connectionSource, VERSION_ENTITY_NAME, CARD_DB_VERSION);
|
||||
boolean isNewBuild = RepositoryUtil.isNewBuildRun(connectionSource, VERSION_ENTITY_NAME, CardRepository.class); // recreate db on new build
|
||||
|
|
@ -65,7 +66,7 @@ public enum CardRepository {
|
|||
}
|
||||
|
||||
TableUtils.createTableIfNotExists(connectionSource, CardInfo.class);
|
||||
cardDao = DaoManager.createDao(connectionSource, CardInfo.class);
|
||||
cardsDao = DaoManager.createDao(connectionSource, CardInfo.class);
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(CardRepository.class).error("Error creating card repository - " + e, e);
|
||||
processMemoryErrors(e);
|
||||
|
|
@ -95,26 +96,22 @@ public enum CardRepository {
|
|||
}
|
||||
|
||||
public void saveCards(final List<CardInfo> newCards, long newContentVersion) {
|
||||
if (newCards == null || newCards.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
cardDao.callBatchTasks(() -> {
|
||||
// add
|
||||
if (newCards != null && !newCards.isEmpty()) {
|
||||
logger.info("DB: need to add " + newCards.size() + " new cards");
|
||||
try {
|
||||
for (CardInfo card : newCards) {
|
||||
cardDao.create(card);
|
||||
if (classNames != null) {
|
||||
classNames.add(card.getClassName());
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(CardRepository.class).error("Error adding cards to DB - " + e, e);
|
||||
processMemoryErrors(e);
|
||||
cardsDao.callBatchTasks(() -> {
|
||||
// only add new cards (no updates)
|
||||
logger.info("DB: need to add " + newCards.size() + " new cards");
|
||||
try {
|
||||
for (CardInfo card : newCards) {
|
||||
cardsDao.create(card);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(CardRepository.class).error("Error adding cards to DB - " + e, e);
|
||||
processMemoryErrors(e);
|
||||
}
|
||||
|
||||
// no card updates
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
|
|
@ -158,9 +155,9 @@ public enum CardRepository {
|
|||
public Set<String> getNames() {
|
||||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName");
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
List<CardInfo> results = cardsDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
addNewNames(card, names);
|
||||
}
|
||||
|
|
@ -174,10 +171,10 @@ public enum CardRepository {
|
|||
public Set<String> getNonLandNames() {
|
||||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName");
|
||||
qb.where().not().like("types", new SelectArg('%' + CardType.LAND.name() + '%'));
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
List<CardInfo> results = cardsDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
addNewNames(card, names);
|
||||
}
|
||||
|
|
@ -191,14 +188,14 @@ public enum CardRepository {
|
|||
public Set<String> getNonbasicLandNames() {
|
||||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName");
|
||||
Where<CardInfo, Object> where = qb.where();
|
||||
where.and(
|
||||
where.not().like("supertypes", '%' + SuperType.BASIC.name() + '%'),
|
||||
where.like("types", '%' + CardType.LAND.name() + '%')
|
||||
);
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
List<CardInfo> results = cardsDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
addNewNames(card, names);
|
||||
}
|
||||
|
|
@ -212,10 +209,10 @@ public enum CardRepository {
|
|||
public Set<String> getNotBasicLandNames() {
|
||||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName");
|
||||
qb.where().not().like("supertypes", new SelectArg('%' + SuperType.BASIC.name() + '%'));
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
List<CardInfo> results = cardsDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
addNewNames(card, names);
|
||||
}
|
||||
|
|
@ -229,10 +226,10 @@ public enum CardRepository {
|
|||
public Set<String> getCreatureNames() {
|
||||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName");
|
||||
qb.where().like("types", new SelectArg('%' + CardType.CREATURE.name() + '%'));
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
List<CardInfo> results = cardsDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
addNewNames(card, names);
|
||||
}
|
||||
|
|
@ -246,10 +243,10 @@ public enum CardRepository {
|
|||
public Set<String> getArtifactNames() {
|
||||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName");
|
||||
qb.where().like("types", new SelectArg('%' + CardType.ARTIFACT.name() + '%'));
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
List<CardInfo> results = cardsDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
addNewNames(card, names);
|
||||
}
|
||||
|
|
@ -263,14 +260,14 @@ public enum CardRepository {
|
|||
public Set<String> getNonLandAndNonCreatureNames() {
|
||||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName");
|
||||
Where<CardInfo, Object> where = qb.where();
|
||||
where.and(
|
||||
where.not().like("types", '%' + CardType.CREATURE.name() + '%'),
|
||||
where.not().like("types", '%' + CardType.LAND.name() + '%')
|
||||
);
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
List<CardInfo> results = cardsDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
addNewNames(card, names);
|
||||
}
|
||||
|
|
@ -284,14 +281,14 @@ public enum CardRepository {
|
|||
public Set<String> getNonArtifactAndNonLandNames() {
|
||||
Set<String> names = new TreeSet<>();
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> qb = cardsDao.queryBuilder();
|
||||
qb.distinct().selectColumns("name", "modalDoubleFacedSecondSideName", "secondSideName", "flipCardName");
|
||||
Where<CardInfo, Object> where = qb.where();
|
||||
where.and(
|
||||
where.not().like("types", '%' + CardType.ARTIFACT.name() + '%'),
|
||||
where.not().like("types", '%' + CardType.LAND.name() + '%')
|
||||
);
|
||||
List<CardInfo> results = cardDao.query(qb.prepare());
|
||||
List<CardInfo> results = cardsDao.query(qb.prepare());
|
||||
for (CardInfo card : results) {
|
||||
addNewNames(card, names);
|
||||
}
|
||||
|
|
@ -308,7 +305,7 @@ public enum CardRepository {
|
|||
|
||||
public CardInfo findCard(String setCode, String cardNumber, boolean ignoreNightCards) {
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> queryBuilder = cardsDao.queryBuilder();
|
||||
if (ignoreNightCards) {
|
||||
queryBuilder.limit(1L).where()
|
||||
.eq("setCode", new SelectArg(setCode))
|
||||
|
|
@ -323,7 +320,7 @@ public enum CardRepository {
|
|||
// (example: vow - 65 - Jacob Hauken, Inspector), so make priority for main side first
|
||||
queryBuilder.orderBy("nightCard", true);
|
||||
}
|
||||
List<CardInfo> result = cardDao.query(queryBuilder.prepare());
|
||||
List<CardInfo> result = cardsDao.query(queryBuilder.prepare());
|
||||
if (!result.isEmpty()) {
|
||||
return result.get(0);
|
||||
}
|
||||
|
|
@ -337,7 +334,7 @@ public enum CardRepository {
|
|||
public List<String> getClassNames() {
|
||||
List<String> names = new ArrayList<>();
|
||||
try {
|
||||
List<CardInfo> results = cardDao.queryForAll();
|
||||
List<CardInfo> results = cardsDao.queryForAll();
|
||||
for (CardInfo card : results) {
|
||||
names.add(card.getClassName());
|
||||
}
|
||||
|
|
@ -350,10 +347,10 @@ public enum CardRepository {
|
|||
|
||||
public List<CardInfo> getMissingCards(List<String> classNames) {
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> queryBuilder = cardsDao.queryBuilder();
|
||||
queryBuilder.where().not().in("className", classNames);
|
||||
|
||||
return cardDao.query(queryBuilder.prepare());
|
||||
return cardsDao.query(queryBuilder.prepare());
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(CardRepository.class).error("Error getting missing cards from DB: " + e, e);
|
||||
processMemoryErrors(e);
|
||||
|
|
@ -424,12 +421,12 @@ public enum CardRepository {
|
|||
* Used for building cubes, packs, and for ensuring that dual faces and split cards have sides/halves from
|
||||
* the same set and variant art.
|
||||
*
|
||||
* @param name name of the card, or side of the card, to find
|
||||
* @param expansion the set name from which to find the card
|
||||
* @param cardNumber the card number for variant arts in one set
|
||||
* @param returnSplitCardHalf whether to return a half of a split card or the corresponding full card.
|
||||
* Want this `false` when user is searching by either names in a split card so that
|
||||
* the full card can be found by either name.
|
||||
* @param name name of the card, or side of the card, to find
|
||||
* @param expansion the set name from which to find the card
|
||||
* @param cardNumber the card number for variant arts in one set
|
||||
* @param returnSplitCardHalf whether to return a half of a split card or the corresponding full card.
|
||||
* Want this `false` when user is searching by either names in a split card so that
|
||||
* the full card can be found by either name.
|
||||
* @return
|
||||
*/
|
||||
public CardInfo findCardWithPreferredSetAndNumber(String name, String expansion, String cardNumber, boolean returnSplitCardHalf) {
|
||||
|
|
@ -461,27 +458,27 @@ public enum CardRepository {
|
|||
* Find a card's reprints from all sets.
|
||||
* It allows for cards to be searched by their full name, or in the case of multi-name cards of the type "A // B"
|
||||
* To search for them using "A", "B", or "A // B".
|
||||
*
|
||||
* <p>
|
||||
* Note of how the function works:
|
||||
* Out of all card types (Split, MDFC, Adventure, Flip, Transform)
|
||||
* ONLY Split cards (Fire // Ice) MUST be queried in the DB by the full name when querying by "name".
|
||||
* Searching for it by either half will return an incorrect result.
|
||||
* ALL the others MUST be queried for by the first half of their full name (i.e. "A" from "A // B")
|
||||
* when querying by "name".
|
||||
* Out of all card types (Split, MDFC, Adventure, Flip, Transform)
|
||||
* ONLY Split cards (Fire // Ice) MUST be queried in the DB by the full name when querying by "name".
|
||||
* Searching for it by either half will return an incorrect result.
|
||||
* ALL the others MUST be queried for by the first half of their full name (i.e. "A" from "A // B")
|
||||
* when querying by "name".
|
||||
*
|
||||
* @param name the name of the card to search for
|
||||
* @param limitByMaxAmount return max amount of different cards (if 0 then return card from all sets)
|
||||
* @param returnSplitCardHalf whether to return a half of a split card or the corresponding full card.
|
||||
* Want this `false` when user is searching by either names in a split card so that
|
||||
* the full card can be found by either name.
|
||||
* Want this `true` when the client is searching for info on both halves to display it.
|
||||
* @canCheckDatabaseHealth try to fix database on any errors (use true anytime except fix methods itself)
|
||||
* @return a list of the reprints of the card if it was found (up to limitByMaxAmount number),
|
||||
* or an empty list if the card was not found.
|
||||
* @param name the name of the card to search for
|
||||
* @param limitByMaxAmount return max amount of different cards (if 0 then return card from all sets)
|
||||
* @param returnSplitCardHalf whether to return a half of a split card or the corresponding full card.
|
||||
* Want this `false` when user is searching by either names in a split card so that
|
||||
* the full card can be found by either name.
|
||||
* Want this `true` when the client is searching for info on both halves to display it.
|
||||
* @return a list of the reprints of the card if it was found (up to limitByMaxAmount number),
|
||||
* or an empty list if the card was not found.
|
||||
* @canCheckDatabaseHealth try to fix database on any errors (use true anytime except fix methods itself)
|
||||
*/
|
||||
public List<CardInfo> findCards(String name, long limitByMaxAmount, boolean returnSplitCardHalf, boolean canCheckDatabaseHealth) {
|
||||
List<CardInfo> results;
|
||||
QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> queryBuilder = cardsDao.queryBuilder();
|
||||
if (limitByMaxAmount > 0) {
|
||||
queryBuilder.limit(limitByMaxAmount);
|
||||
}
|
||||
|
|
@ -492,27 +489,27 @@ public enum CardRepository {
|
|||
// Could be made faster by searching assuming it's NOT a split card and first searching by the first
|
||||
// half of the name, but this is easier to understand.
|
||||
queryBuilder.where().eq("name", new SelectArg(name));
|
||||
results = cardDao.query(queryBuilder.prepare());
|
||||
results = cardsDao.query(queryBuilder.prepare());
|
||||
|
||||
// Result comes back empty, try to search using the first half (could be Adventure, MDFC, etc.)
|
||||
if (results.isEmpty()) {
|
||||
String mainCardName = name.split(" // ", 2)[0];
|
||||
queryBuilder.where().eq("name", new SelectArg(mainCardName));
|
||||
results = cardDao.query(queryBuilder.prepare()); // If still empty, then card can't be found
|
||||
results = cardsDao.query(queryBuilder.prepare()); // If still empty, then card can't be found
|
||||
}
|
||||
} else { // Cannot tell if string represents the full name of a card or only part of it.
|
||||
// Assume it is the full card name
|
||||
queryBuilder.where().eq("name", new SelectArg(name));
|
||||
results = cardDao.query(queryBuilder.prepare());
|
||||
results = cardsDao.query(queryBuilder.prepare());
|
||||
|
||||
if (results.isEmpty()) {
|
||||
// Nothing found when looking for main name, try looking under the other names
|
||||
queryBuilder.where()
|
||||
.eq("flipCardName", new SelectArg(name)).or()
|
||||
.eq("secondSideName", new SelectArg(name)).or()
|
||||
.eq("adventureSpellName", new SelectArg(name)).or()
|
||||
.eq("modalDoubleFacedSecondSideName", new SelectArg(name));
|
||||
results = cardDao.query(queryBuilder.prepare());
|
||||
.eq("flipCardName", new SelectArg(name)).or()
|
||||
.eq("secondSideName", new SelectArg(name)).or()
|
||||
.eq("adventureSpellName", new SelectArg(name)).or()
|
||||
.eq("modalDoubleFacedSecondSideName", new SelectArg(name));
|
||||
results = cardsDao.query(queryBuilder.prepare());
|
||||
} else {
|
||||
// Check that a full card was found and not a SplitCardHalf
|
||||
// Can be caused by searching for "Fire" instead of "Fire // Ice"
|
||||
|
|
@ -522,7 +519,7 @@ public enum CardRepository {
|
|||
queryBuilder.where()
|
||||
.eq("setCode", new SelectArg(firstCardInfo.setCode)).and()
|
||||
.eq("cardNumber", new SelectArg(firstCardInfo.cardNumber));
|
||||
List<CardInfo> tmpResults = cardDao.query(queryBuilder.prepare());
|
||||
List<CardInfo> tmpResults = cardsDao.query(queryBuilder.prepare());
|
||||
|
||||
String fullSplitCardName = null;
|
||||
for (CardInfo cardInfo : tmpResults) {
|
||||
|
|
@ -536,7 +533,7 @@ public enum CardRepository {
|
|||
}
|
||||
|
||||
queryBuilder.where().eq("name", new SelectArg(fullSplitCardName));
|
||||
results = cardDao.query(queryBuilder.prepare());
|
||||
results = cardsDao.query(queryBuilder.prepare());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -557,9 +554,9 @@ public enum CardRepository {
|
|||
|
||||
public List<CardInfo> findCardsByClass(String canonicalClassName) {
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> queryBuilder = cardsDao.queryBuilder();
|
||||
queryBuilder.where().eq("className", new SelectArg(canonicalClassName));
|
||||
return cardDao.query(queryBuilder.prepare());
|
||||
return cardsDao.query(queryBuilder.prepare());
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(CardRepository.class).error("Error during execution of raw sql statement" + e, e);
|
||||
processMemoryErrors(e);
|
||||
|
|
@ -570,7 +567,7 @@ public enum CardRepository {
|
|||
/**
|
||||
* Warning, don't use db functions in card's code - it generates heavy db loading in AI simulations. If you
|
||||
* need that feature then check for simulation mode. See https://github.com/magefree/mage/issues/7014
|
||||
*
|
||||
* <p>
|
||||
* Ignoring night cards by default
|
||||
*
|
||||
* @param criteria
|
||||
|
|
@ -578,10 +575,10 @@ public enum CardRepository {
|
|||
*/
|
||||
public List<CardInfo> findCards(CardCriteria criteria) {
|
||||
try {
|
||||
QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder();
|
||||
QueryBuilder<CardInfo, Object> queryBuilder = cardsDao.queryBuilder();
|
||||
criteria.buildQuery(queryBuilder);
|
||||
|
||||
return cardDao.query(queryBuilder.prepare());
|
||||
return cardsDao.query(queryBuilder.prepare());
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(CardRepository.class).error("Error during execution of card repository query statement: " + e, e);
|
||||
processMemoryErrors(e);
|
||||
|
|
@ -622,7 +619,7 @@ public enum CardRepository {
|
|||
|
||||
public long getContentVersionFromDB() {
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_CARDS, false));
|
||||
return RepositoryUtil.getDatabaseVersion(connectionSource, VERSION_ENTITY_NAME + "Content");
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(CardRepository.class).error("Error getting content version from DB - " + e, e);
|
||||
|
|
@ -633,7 +630,7 @@ public enum CardRepository {
|
|||
|
||||
public void setContentVersion(long version) {
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_CARDS, false));
|
||||
RepositoryUtil.updateVersion(connectionSource, VERSION_ENTITY_NAME + "Content", version);
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(CardRepository.class).error("Error setting content version - " + e, e);
|
||||
|
|
@ -645,25 +642,84 @@ public enum CardRepository {
|
|||
return CARD_CONTENT_VERSION;
|
||||
}
|
||||
|
||||
public void closeDB() {
|
||||
public void closeDB(boolean writeCompact) {
|
||||
try {
|
||||
if (cardDao != null && cardDao.getConnectionSource() != null) {
|
||||
DatabaseConnection conn = cardDao.getConnectionSource().getReadWriteConnection(cardDao.getTableName());
|
||||
conn.executeStatement("shutdown compact", 0);
|
||||
if (cardsDao != null && cardsDao.getConnectionSource() != null) {
|
||||
DatabaseConnection conn = cardsDao.getConnectionSource().getReadWriteConnection(cardsDao.getTableName());
|
||||
if (writeCompact) {
|
||||
conn.executeStatement("SHUTDOWN COMPACT", 0); // compact data and rewrite whole db
|
||||
} else {
|
||||
conn.executeStatement("SHUTDOWN IMMEDIATELY", 0); // close without any writes
|
||||
}
|
||||
cardsDao.getConnectionSource().releaseConnection(conn);
|
||||
}
|
||||
} catch (SQLException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
private void openDB() {
|
||||
public void openDB() {
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
cardDao = DaoManager.createDao(connectionSource, CardInfo.class);
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_CARDS, true));
|
||||
cardsDao = DaoManager.createDao(connectionSource, CardInfo.class);
|
||||
} catch (SQLException e) {
|
||||
Logger.getLogger(CardRepository.class).error("Error opening card repository - " + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void printDatabaseStats(String info) {
|
||||
List<List<String>> allSettings = querySQL("SELECT NAME, VALUE FROM INFORMATION_SCHEMA.SETTINGS");
|
||||
if (allSettings == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// cache
|
||||
logger.info("Database cache settings (" + info + "):");
|
||||
allSettings.stream().filter(values -> values.get(0).equals("CACHE_SIZE")).forEach(values -> {
|
||||
logger.info(" - cache size, setup: " + values.get(1) + " kb");
|
||||
});
|
||||
allSettings.stream().filter(values -> values.get(0).equals("info.CACHE_MAX_SIZE")).forEach(values -> {
|
||||
logger.info(" - cache size, max: " + values.get(1) + " mb");
|
||||
});
|
||||
allSettings.stream().filter(values -> values.get(0).equals("info.CACHE_SIZE")).forEach(values -> {
|
||||
logger.info(" - cache size, current: " + values.get(1) + " mb");
|
||||
});
|
||||
|
||||
// memory
|
||||
allSettings = querySQL("SELECT MEMORY_FREE(), MEMORY_USED()");
|
||||
if (allSettings == null) {
|
||||
return;
|
||||
}
|
||||
logger.info("Database memory stats (" + info + "):");
|
||||
logger.info(" - free: " + allSettings.get(0).get(0) + " kb");
|
||||
logger.info(" - used: " + allSettings.get(0).get(1) + " kb");
|
||||
}
|
||||
|
||||
/**
|
||||
* Exec any SQL query and return result table as string values
|
||||
*/
|
||||
public List<List<String>> querySQL(String sql) {
|
||||
try {
|
||||
GenericRawResults<String[]> query = cardsDao.queryRaw(sql);
|
||||
return query.getResults().stream()
|
||||
.map(Arrays::asList)
|
||||
.collect(Collectors.toList());
|
||||
} catch (SQLException e) {
|
||||
logger.error("Can't query sql due error: " + sql + " - " + e, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exec any SQL code without result. Can be used to change db settings like SET xxx = YYY
|
||||
*/
|
||||
public void execSQL(String sql) {
|
||||
try {
|
||||
cardsDao.executeRaw(sql);
|
||||
} catch (SQLException e) {
|
||||
logger.error("Can't exec sql due error: " + sql + " - " + e, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static CardInfo safeFindKnownCard() {
|
||||
// safe find of known card with memory/db fixes
|
||||
return instance.findCards("Silvercoat Lion", 1, false, false)
|
||||
|
|
@ -690,7 +746,7 @@ public enum CardRepository {
|
|||
}
|
||||
|
||||
// DB seems to have a problem - try to restart the DB (useless in 99% due out of memory problems)
|
||||
instance.closeDB();
|
||||
instance.closeDB(false);
|
||||
instance.openDB();
|
||||
cardInfo = safeFindKnownCard();
|
||||
if (cardInfo != null) {
|
||||
|
|
|
|||
69
Mage/src/main/java/mage/cards/repository/DatabaseUtils.java
Normal file
69
Mage/src/main/java/mage/cards/repository/DatabaseUtils.java
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package mage.cards.repository;
|
||||
|
||||
import mage.util.DebugUtil;
|
||||
|
||||
/**
|
||||
* Helper class for database
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class DatabaseUtils {
|
||||
|
||||
// warning, do not change names or db format
|
||||
// h2
|
||||
public static final String DB_NAME_FEEDBACK = "feedback.h2";
|
||||
public static final String DB_NAME_USERS = "authorized_user.h2";
|
||||
public static final String DB_NAME_CARDS = "cards.h2";
|
||||
// sqlite (usage reason: h2 database works bad with 1GB+ files and can break it)
|
||||
public static final String DB_NAME_RECORDS = "table_record.db";
|
||||
public static final String DB_NAME_STATS = "user_stats.db";
|
||||
|
||||
/**
|
||||
* Prepare JDBC connection string and setup additional params for H2 databases
|
||||
*
|
||||
* @param dbName database name like "cards.h2"
|
||||
* @param improveCaches use memory optimizations for cards database (no needs for other dbs)
|
||||
*/
|
||||
public static String prepareH2Connection(String dbName, boolean improveCaches) {
|
||||
// example: jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE;IGNORECASE=TRUE
|
||||
String res = String.format("jdbc:h2:file:./db/%s", dbName);
|
||||
|
||||
// shared params
|
||||
res += ";AUTO_SERVER=TRUE"; // open database in mix mode (first open by new thread, second open by new jvm-process)
|
||||
res += ";IGNORECASE=TRUE"; // ignore char case for text searching
|
||||
|
||||
// additional params
|
||||
// can be defined by connection string, by exec sql like "SET xxx = yyy", by settings from existing db-file
|
||||
|
||||
if (improveCaches) {
|
||||
// CACHE_SIZE
|
||||
// max query cache size in kb (default: 65 Mb per 1 GB of java's max memory)
|
||||
// warning, xmage require 150Mb cache for big queries in AI games like all card names (db can be broken on lower cache)
|
||||
//res += ";CACHE_SIZE=150000";
|
||||
res += ";CACHE_SIZE=" + Math.round(Math.max(150000, Runtime.getRuntime().maxMemory() * 0.1 / 1024));
|
||||
|
||||
|
||||
// QUERY_CACHE_SIZE
|
||||
// queries amount per session to cache (default: 8)
|
||||
res += ";QUERY_CACHE_SIZE=32";
|
||||
}
|
||||
|
||||
// add debug stats (see DebugUtil for usage instruction)
|
||||
if (DebugUtil.DATABASE_PROFILE_SQL_QUERIES_TO_FILE) {
|
||||
res += ";TRACE_LEVEL_FILE=2";
|
||||
res += ";QUERY_STATISTICS=TRUE";
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare JDBC connection string and setup additional params for SQLite databases
|
||||
*
|
||||
* @param dbName database name like "cards"
|
||||
*/
|
||||
public static String prepareSqliteConnection(String dbName) {
|
||||
// example: jdbc:sqlite:./db/table_record.db
|
||||
return String.format("jdbc:sqlite:./db/%s", dbName);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,13 +27,13 @@ public enum ExpansionRepository {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(ExpansionRepository.class);
|
||||
|
||||
private static final String JDBC_URL = "jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE;IGNORECASE=TRUE";
|
||||
// TODO: delete db version from cards and expansions due un-used (that's dbs re-created on each update)
|
||||
private static final String VERSION_ENTITY_NAME = "expansion";
|
||||
private static final long EXPANSION_DB_VERSION = 5;
|
||||
private static final long EXPANSION_CONTENT_VERSION = 18;
|
||||
|
||||
private Dao<ExpansionInfo, Object> expansionDao;
|
||||
private RepositoryEventSource eventSource = new RepositoryEventSource();
|
||||
private final RepositoryEventSource eventSource = new RepositoryEventSource();
|
||||
public boolean instanceInitialized = false;
|
||||
|
||||
ExpansionRepository() {
|
||||
|
|
@ -42,7 +42,7 @@ public enum ExpansionRepository {
|
|||
file.mkdirs();
|
||||
}
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_CARDS, true));
|
||||
|
||||
boolean isObsolete = RepositoryUtil.isDatabaseObsolete(connectionSource, VERSION_ENTITY_NAME, EXPANSION_DB_VERSION);
|
||||
boolean isNewBuild = RepositoryUtil.isNewBuildRun(connectionSource, VERSION_ENTITY_NAME, ExpansionRepository.class); // recreate db on new build
|
||||
|
|
@ -218,7 +218,7 @@ public enum ExpansionRepository {
|
|||
|
||||
public long getContentVersionFromDB() {
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_CARDS, false));
|
||||
return RepositoryUtil.getDatabaseVersion(connectionSource, VERSION_ENTITY_NAME + "Content");
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
|
|
@ -228,7 +228,7 @@ public enum ExpansionRepository {
|
|||
|
||||
public void setContentVersion(long version) {
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_CARDS, false));
|
||||
RepositoryUtil.updateVersion(connectionSource, VERSION_ENTITY_NAME + "Content", version);
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error setting content version - " + e, e);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import java.util.EventObject;
|
|||
*/
|
||||
public class RepositoryEvent extends EventObject implements ExternalEvent, Serializable {
|
||||
|
||||
// TODO: db changed on update only, events can be deleted
|
||||
public enum RepositoryEventType {
|
||||
DB_LOADED, DB_UPDATED
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.j256.ormlite.stmt.QueryBuilder;
|
|||
import com.j256.ormlite.stmt.SelectArg;
|
||||
import com.j256.ormlite.support.ConnectionSource;
|
||||
import com.j256.ormlite.table.TableUtils;
|
||||
import mage.util.DebugUtil;
|
||||
import mage.util.JarVersion;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
|
@ -39,22 +40,26 @@ public final class RepositoryUtil {
|
|||
logger.info(" - emblems: " + TokenRepository.instance.getByType(TokenType.EMBLEM).size());
|
||||
logger.info(" - planes: " + TokenRepository.instance.getByType(TokenType.PLANE).size());
|
||||
logger.info(" - dungeons: " + TokenRepository.instance.getByType(TokenType.DUNGEON).size());
|
||||
|
||||
if (DebugUtil.DATABASE_SHOW_CACHE_AND_MEMORY_STATS_ON_STARTUP) {
|
||||
CardRepository.instance.printDatabaseStats("on startup");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isDatabaseObsolete(ConnectionSource connectionSource, String entityName, long version) throws SQLException {
|
||||
TableUtils.createTableIfNotExists(connectionSource, DatabaseVersion.class);
|
||||
Dao<DatabaseVersion, Object> dbVersionDao = DaoManager.createDao(connectionSource, DatabaseVersion.class);
|
||||
Dao<DatabaseVersion, Object> versionDao = DaoManager.createDao(connectionSource, DatabaseVersion.class);
|
||||
|
||||
QueryBuilder<DatabaseVersion, Object> queryBuilder = dbVersionDao.queryBuilder();
|
||||
QueryBuilder<DatabaseVersion, Object> queryBuilder = versionDao.queryBuilder();
|
||||
queryBuilder.where().eq("entity", new SelectArg(entityName))
|
||||
.and().eq("version", new SelectArg(version));
|
||||
List<DatabaseVersion> dbVersions = dbVersionDao.query(queryBuilder.prepare());
|
||||
List<DatabaseVersion> dbVersions = versionDao.query(queryBuilder.prepare());
|
||||
|
||||
if (dbVersions.isEmpty()) {
|
||||
DatabaseVersion dbVersion = new DatabaseVersion();
|
||||
dbVersion.setEntity(entityName);
|
||||
dbVersion.setVersion(version);
|
||||
dbVersionDao.create(dbVersion);
|
||||
versionDao.create(dbVersion);
|
||||
}
|
||||
return dbVersions.isEmpty();
|
||||
}
|
||||
|
|
@ -68,48 +73,48 @@ public final class RepositoryUtil {
|
|||
}
|
||||
|
||||
TableUtils.createTableIfNotExists(connectionSource, DatabaseBuild.class);
|
||||
Dao<DatabaseBuild, Object> dbBuildDao = DaoManager.createDao(connectionSource, DatabaseBuild.class);
|
||||
Dao<DatabaseBuild, Object> buildDao = DaoManager.createDao(connectionSource, DatabaseBuild.class);
|
||||
|
||||
QueryBuilder<DatabaseBuild, Object> queryBuilder = dbBuildDao.queryBuilder();
|
||||
QueryBuilder<DatabaseBuild, Object> queryBuilder = buildDao.queryBuilder();
|
||||
queryBuilder.where().eq("entity", new SelectArg(entityName))
|
||||
.and().eq("last_build", new SelectArg(currentBuild));
|
||||
List<DatabaseBuild> dbBuilds = dbBuildDao.query(queryBuilder.prepare());
|
||||
List<DatabaseBuild> dbBuilds = buildDao.query(queryBuilder.prepare());
|
||||
|
||||
if (dbBuilds.isEmpty()) {
|
||||
DatabaseBuild dbBuild = new DatabaseBuild();
|
||||
dbBuild.setEntity(entityName);
|
||||
dbBuild.setLastBuild(currentBuild);
|
||||
dbBuildDao.create(dbBuild);
|
||||
buildDao.create(dbBuild);
|
||||
}
|
||||
return dbBuilds.isEmpty();
|
||||
}
|
||||
|
||||
public static void updateVersion(ConnectionSource connectionSource, String entityName, long version) throws SQLException {
|
||||
TableUtils.createTableIfNotExists(connectionSource, DatabaseVersion.class);
|
||||
Dao<DatabaseVersion, Object> dbVersionDao = DaoManager.createDao(connectionSource, DatabaseVersion.class);
|
||||
Dao<DatabaseVersion, Object> versionDao = DaoManager.createDao(connectionSource, DatabaseVersion.class);
|
||||
|
||||
QueryBuilder<DatabaseVersion, Object> queryBuilder = dbVersionDao.queryBuilder();
|
||||
QueryBuilder<DatabaseVersion, Object> queryBuilder = versionDao.queryBuilder();
|
||||
queryBuilder.where().eq("entity", new SelectArg(entityName));
|
||||
List<DatabaseVersion> dbVersions = dbVersionDao.query(queryBuilder.prepare());
|
||||
List<DatabaseVersion> dbVersions = versionDao.query(queryBuilder.prepare());
|
||||
|
||||
if (!dbVersions.isEmpty()) {
|
||||
DeleteBuilder<DatabaseVersion, Object> deleteBuilder = dbVersionDao.deleteBuilder();
|
||||
DeleteBuilder<DatabaseVersion, Object> deleteBuilder = versionDao.deleteBuilder();
|
||||
deleteBuilder.where().eq("entity", new SelectArg(entityName));
|
||||
deleteBuilder.delete();
|
||||
}
|
||||
DatabaseVersion databaseVersion = new DatabaseVersion();
|
||||
databaseVersion.setEntity(entityName);
|
||||
databaseVersion.setVersion(version);
|
||||
dbVersionDao.create(databaseVersion);
|
||||
versionDao.create(databaseVersion);
|
||||
}
|
||||
|
||||
public static long getDatabaseVersion(ConnectionSource connectionSource, String entityName) throws SQLException {
|
||||
TableUtils.createTableIfNotExists(connectionSource, DatabaseVersion.class);
|
||||
Dao<DatabaseVersion, Object> dbVersionDao = DaoManager.createDao(connectionSource, DatabaseVersion.class);
|
||||
Dao<DatabaseVersion, Object> versionDao = DaoManager.createDao(connectionSource, DatabaseVersion.class);
|
||||
|
||||
QueryBuilder<DatabaseVersion, Object> queryBuilder = dbVersionDao.queryBuilder();
|
||||
QueryBuilder<DatabaseVersion, Object> queryBuilder = versionDao.queryBuilder();
|
||||
queryBuilder.where().eq("entity", new SelectArg(entityName));
|
||||
List<DatabaseVersion> dbVersions = dbVersionDao.query(queryBuilder.prepare());
|
||||
List<DatabaseVersion> dbVersions = versionDao.query(queryBuilder.prepare());
|
||||
if (dbVersions.isEmpty()) {
|
||||
return 0;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,18 @@ public class DebugUtil {
|
|||
// game dialogs
|
||||
public static boolean GUI_GAME_DIALOGS_DRAW_CARDS_AREA_BORDER = false;
|
||||
|
||||
// database - show additional info about cache and memory settings
|
||||
public static boolean DATABASE_SHOW_CACHE_AND_MEMORY_STATS_ON_STARTUP = true;
|
||||
|
||||
// database - collect sql queries and stats
|
||||
// how-to use:
|
||||
// - clean db folders or delete all *.trace.db files
|
||||
// - run tests or real server to collect some stats
|
||||
// - download h2 files for ver 1.4.197 from https://h2database.com/h2-2018-03-18.zip and open tools folder like xxx\H2\bin
|
||||
// - execute command: java -cp "h2-1.4.196.jar;%H2DRIVERS%;%CLASSPATH%" org.h2.tools.ConvertTraceFile -traceFile "xxx\Mage.Tests\db\cards.h2.trace.db" -script "xxx\Mage.Tests\db\cards.h2.trace.sql"
|
||||
// - open *.sql file for all sql-queries and exec stats
|
||||
public static boolean DATABASE_PROFILE_SQL_QUERIES_TO_FILE = false;
|
||||
|
||||
public static String getMethodNameWithSource(final int depth) {
|
||||
return TraceHelper.getMethodNameWithSource(depth);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue