diff --git a/gradle.properties b/gradle.properties index 25a7f34..795f631 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ ## Project Configurations # Proj Metadata -projVersion = 0.7 +projVersion = 0.8 # Dependencies diff --git a/sekai-cli/gradle.properties b/sekai-cli/gradle.properties index e055f71..51ec46a 100644 --- a/sekai-cli/gradle.properties +++ b/sekai-cli/gradle.properties @@ -1,7 +1,7 @@ ## Project Configurations # Proj Metadata -moduleVersion = 0.5.3 +moduleVersion = 0.5.4 # dependencies lib_postgres_driver = 42.5.1 diff --git a/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/client/Database.java b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/client/Database.java index 5f4baeb..59113d1 100644 --- a/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/client/Database.java +++ b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/client/Database.java @@ -1,6 +1,9 @@ package cc.sukazyo.sekai_cli.client; +import cc.sukazyo.sekai_cli.ClientMain; import cc.sukazyo.sekai_cli.data_tool.sekai_master_db.Music; +import cc.sukazyo.sekai_db.PostgresSession; +import cc.sukazyo.sekai_db.table.SekaiSongs; import cc.sukazyo.sekai_scores.Song; import com.google.gson.JsonIOException; import com.google.gson.JsonSyntaxException; @@ -11,6 +14,7 @@ import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import java.nio.file.Path; import java.nio.file.Paths; +import java.sql.SQLException; import java.util.ArrayList; import java.util.List; @@ -60,10 +64,27 @@ public class Database { } switch ($args.remove(0)) { case "song" -> { - try { + try (final PostgresSession session = ClientMain.config().db().connect()) { final Song[] songs = Music.toSongArray(Music.readFrom(file.toFile(), charset)); + for (Song i : songs) { + _user(String.format("db_import: start insertion for song #%d (%s)", i.id(), i.name())); + try { + if (SekaiSongs.as(session).hasSong(i.id())) + _user(String.format("db_import: song #%d already exists, skipped.", i.id())); + else { + final int changes = SekaiSongs.as(session).insert(i); + _user(String.format("db_import: song #%d insert succeed: %d row updated.", i.id(), changes)); + } + } catch (SQLException e) { + _user("db_import: song #"+i.id()+": data insert failed: " + e.getMessage()); + _debug(e); + } + } } catch (IOException | JsonIOException | JsonSyntaxException e) { - _user("error while parsing song file: " + e.getMessage()); + _user("db_import: error while parsing song file: " + e.getMessage()); + _debug(e); + } catch (SQLException e) { + _user("db_import: error while connecting to database: " + e.getMessage()); _debug(e); } } diff --git a/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/data_tool/sekai_master_db/MusicDifficulty.java b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/data_tool/sekai_master_db/MusicDifficulty.java new file mode 100644 index 0000000..85afc00 --- /dev/null +++ b/sekai-cli/src/main/java/cc/sukazyo/sekai_cli/data_tool/sekai_master_db/MusicDifficulty.java @@ -0,0 +1,26 @@ +package cc.sukazyo.sekai_cli.data_tool.sekai_master_db; + +import com.google.gson.Gson; +import com.google.gson.JsonIOException; +import com.google.gson.JsonSyntaxException; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.charset.Charset; + +public class MusicDifficulty { + + public int id; + public int musicId; + public String musicDifficulty; + public int playLevel; + public int releaseConditionId; + public int noteCount; + + public static MusicDifficulty[] readFrom (File sekaiMusicDifficultiesFile, Charset charset) + throws IOException, JsonIOException, JsonSyntaxException { + return new Gson().fromJson(new FileReader(sekaiMusicDifficultiesFile, charset), MusicDifficulty[].class); + } + +} diff --git a/sekai-database/build.gradle b/sekai-database/build.gradle index 52c6e30..0599fb2 100644 --- a/sekai-database/build.gradle +++ b/sekai-database/build.gradle @@ -13,6 +13,8 @@ dependencies { compileOnly "com.github.spotbugs:spotbugs-annotations:${lib_spotbugs_v}" + implementation rootProject + testImplementation "org.junit.jupiter:junit-jupiter-api:${lib_junit_v}" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${lib_junit_v}" diff --git a/sekai-database/src/main/java/cc/sukazyo/sekai_db/PostgresConfig.java b/sekai-database/src/main/java/cc/sukazyo/sekai_db/PostgresConfig.java index 6e91296..ee71c46 100644 --- a/sekai-database/src/main/java/cc/sukazyo/sekai_db/PostgresConfig.java +++ b/sekai-database/src/main/java/cc/sukazyo/sekai_db/PostgresConfig.java @@ -38,7 +38,11 @@ public class PostgresConfig { return (schema == null ? "" : '"' + schema() + '"' + ".") + '"' + table + '"'; } - public Connection connect () throws SQLException { + public PostgresSession connect () throws SQLException { + return PostgresSession.as(this); + } + + Connection getConnection () throws SQLException { return DriverManager.getConnection(this.toString(), user, token); } diff --git a/sekai-database/src/main/java/cc/sukazyo/sekai_db/PostgresSession.java b/sekai-database/src/main/java/cc/sukazyo/sekai_db/PostgresSession.java new file mode 100644 index 0000000..91081d7 --- /dev/null +++ b/sekai-database/src/main/java/cc/sukazyo/sekai_db/PostgresSession.java @@ -0,0 +1,28 @@ +package cc.sukazyo.sekai_db; + +import java.sql.Connection; +import java.sql.SQLException; + +public class PostgresSession implements AutoCloseable { + + public final Connection session; + private final PostgresConfig config; + private PostgresSession (Connection session, PostgresConfig config) { + this.config = config; + this.session = session; + } + static PostgresSession as (PostgresConfig config) throws SQLException { + final PostgresSession $this = new PostgresSession(config.getConnection(), config); + $this.session.setAutoCommit(false); + $this.session.beginRequest(); + $this.session.setSchema($this.config.schema); + return $this; + } + + @Override + public void close () throws SQLException { + session.commit(); + session.close(); + } + +} diff --git a/sekai-database/src/main/java/cc/sukazyo/sekai_db/table/SekaiSongDifficulties.java b/sekai-database/src/main/java/cc/sukazyo/sekai_db/table/SekaiSongDifficulties.java new file mode 100644 index 0000000..08ba5f8 --- /dev/null +++ b/sekai-database/src/main/java/cc/sukazyo/sekai_db/table/SekaiSongDifficulties.java @@ -0,0 +1,32 @@ +package cc.sukazyo.sekai_db.table; + +import cc.sukazyo.sekai_db.PostgresSession; +import cc.sukazyo.sekai_db.type.SekaiDifficulties; + +import javax.annotation.Nonnull; + +public class SekaiSongDifficulties { + + public record DatabaseStruct ( + int songId, + @Nonnull SekaiDifficulties difficulty, + int level, + int notes, + int lvlp, + int flvlp, + int plvlp + ) {} + + private final PostgresSession session; + + private SekaiSongDifficulties (PostgresSession session) { this.session = session; } + static SekaiSongDifficulties as (PostgresSession session) { return new SekaiSongDifficulties(session); } + +// public boolean contains (int songId, SekaiDifficulties difficulty) { +// +// } +// +// public int insertDatabaseStructData (DatabaseStruct data) { +// } +// +} diff --git a/sekai-database/src/main/java/cc/sukazyo/sekai_db/table/SekaiSongs.java b/sekai-database/src/main/java/cc/sukazyo/sekai_db/table/SekaiSongs.java new file mode 100644 index 0000000..828ef73 --- /dev/null +++ b/sekai-database/src/main/java/cc/sukazyo/sekai_db/table/SekaiSongs.java @@ -0,0 +1,55 @@ +package cc.sukazyo.sekai_db.table; + +import cc.sukazyo.sekai_db.PostgresSession; +import cc.sukazyo.sekai_scores.Song; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Arrays; + +public class SekaiSongs { + + private final PostgresSession session; + + private SekaiSongs(PostgresSession session) { + this.session = session; + } + public static SekaiSongs as (PostgresSession session) { + return new SekaiSongs(session); + } + + public boolean hasSong(int songId) throws SQLException { + final PreparedStatement statement = session.session.prepareStatement(""" + select id from sekai_songs + where id = ? + """); + statement.setInt(1, songId); + return statement.executeQuery().next(); + } + + public int insert (Song song) throws SQLException { + final PreparedStatement statement = session.session.prepareStatement(""" + insert into sekai_songs + (id, unit_seq, name, producer, arranger, composer, lyricist, bpm, duration, release_date, name_alias) + values (?, ?, ?, ?, ?, ?, ?, ?, cast(? AS interval), ?, ?) + """); + statement.setInt(1, song.id()); + statement.setInt(2, song.unit().seq); + statement.setString(3, song.name()); + statement.setString(4, song.producer()); + statement.setString(5, song.arranger()); + statement.setString(6, song.composer()); + statement.setString(7, song.lyricist()); + if (song.bpm() == null) statement.setNull(8, Types.ARRAY); + else statement.setArray(8, session.session.createArrayOf("smallint", Arrays.stream(song.bpm()).boxed().toArray())); + if (song.duration() == null) statement.setNull(9, Types.VARCHAR); + else statement.setString(9, song.duration().toString()); + if (song.releaseDate() == null) statement.setNull(10, Types.TIMESTAMP_WITH_TIMEZONE); + else statement.setTimestamp(10, Timestamp.from(song.releaseDate().toInstant())); + statement.setArray(11, session.session.createArrayOf("text", song.nameAlias())); + return statement.executeUpdate(); + } + +} diff --git a/sekai-database/src/main/java/cc/sukazyo/sekai_db/type/SekaiDifficulties.java b/sekai-database/src/main/java/cc/sukazyo/sekai_db/type/SekaiDifficulties.java new file mode 100644 index 0000000..b38400e --- /dev/null +++ b/sekai-database/src/main/java/cc/sukazyo/sekai_db/type/SekaiDifficulties.java @@ -0,0 +1,9 @@ +package cc.sukazyo.sekai_db.type; + +public enum SekaiDifficulties { + EASY, + NORMAL, + HARD, + EXPERT, + MASTER +}