Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9f59880
feat: Spring Data JDBCのCRUD解析を追加
irof Feb 20, 2026
43920e1
refactor: 変な位置に作ってたので移動
irof Feb 20, 2026
4057a22
docs: 何者かを書いておく
irof Feb 20, 2026
9fd8852
refactor: SpringDataJdbcStatementsReaderの戻り値を中立化
irof Feb 21, 2026
526c25c
refactor: SQLステートメント型名を中立化
irof Feb 21, 2026
667a223
refactor: SpringDataJdbcStatementsReaderの戻り値をSqlStatementsに統一
irof Feb 21, 2026
277bd4a
refactor: SpringDataJDBC解析をASMメタ情報ベースに変更
irof Feb 21, 2026
af1be40
docs: 推測メソッドにコメントを追加
irof Feb 21, 2026
f071a35
docs: SpringDataJDBC解析対象の条件コメントを追加
irof Feb 22, 2026
59b87f9
fix: @Queryメソッドを命名規約推測の対象外にする
irof Feb 22, 2026
e83ff0a
fix: SpringDataJDBC解析の@Repository条件を除去
irof Feb 22, 2026
38ec9cb
feat: SpringDataJDBCの@Queryメソッド解析に対応
irof Feb 22, 2026
1cf7139
fix: @Query先頭装飾を除去してSQL種別を判定
irof Feb 22, 2026
ff1f39a
docs: SqlStatementIdクラスにTODOコメントを追加
irof Feb 22, 2026
183b72f
refactor: SQLステートメントIDの生成処理をメソッドに分離
irof Feb 22, 2026
298ebb0
fix: SQL解析失敗時のログメッセージを修正
irof Feb 22, 2026
27a4b0b
refactor: DBアクセス判定処理をメソッドに分離
irof Feb 22, 2026
4092235
test: SpringDataJdbcStatementReaderTestをパラメタライズド化
irof Feb 22, 2026
dae8953
refactor: QueryからのSQL種別推測をSqlTypeに集約
irof Feb 22, 2026
0c86a8d
refactor: SQL先頭装飾の除去処理をQueryに集約
irof Feb 22, 2026
7c31ec4
refactor: inferSqlTypeFromQueryがQueryを受け取るよう変更
irof Feb 22, 2026
f4472e5
refactor: QueryにnormalizedQueryを追加
irof Feb 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ public OutputImplementations outputImplementations(JigRepository jigRepository)
public DatasourceAngles datasourceAngles(JigRepository jigRepository) {
var jigTypes = typesQueryService.jigTypes(jigRepository);
var outputImplementations = outputImplementations(jigRepository);
return DatasourceAngles.from(outputImplementations, jigRepository.jigDataProvider().fetchMybatisStatements(), MethodRelations.from(jigTypes));
return DatasourceAngles.from(outputImplementations, jigRepository.jigDataProvider().fetchSqlStatements(), MethodRelations.from(jigTypes));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.dddjava.jig.domain.model.data;

import org.dddjava.jig.domain.model.data.enums.EnumModels;
import org.dddjava.jig.domain.model.data.rdbaccess.MyBatisStatements;
import org.dddjava.jig.domain.model.data.rdbaccess.SqlStatements;

import java.util.List;

Expand All @@ -10,8 +10,8 @@ public interface JigDataProvider {
static JigDataProvider none() {
return new JigDataProvider() {
@Override
public MyBatisStatements fetchMybatisStatements() {
return MyBatisStatements.empty();
public SqlStatements fetchSqlStatements() {
return SqlStatements.empty();
}

@Override
Expand All @@ -21,7 +21,7 @@ public EnumModels fetchEnumModels() {
};
}

MyBatisStatements fetchMybatisStatements();
SqlStatements fetchSqlStatements();

EnumModels fetchEnumModels();
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,70 @@
/**
* クエリ
*/
public record Query(String text) {
public record Query(String rawText, String normalizedQuery) {

public static final String UNSUPPORTED = "<<unsupported>>";

public static Query from(@Nullable String text) {
if (text == null) return unsupported();
return new Query(text);
var normalizedQuery = normalizeSql(text);
if (normalizedQuery.isEmpty()) return unsupported();
return new Query(text, normalizedQuery);
}

public static Query unsupported() {
return new Query(UNSUPPORTED);
return new Query(UNSUPPORTED, UNSUPPORTED);
}

@Override
public String text() {
if (UNSUPPORTED.equals(text)) {
public String rawText() {
if (UNSUPPORTED.equals(rawText)) {
// 特殊値を返さないようにする
// Queryのtextは外部から使用しないので例外でよい。これが発生したらバグ。
throw new IllegalArgumentException("BUG!!");
}
return text;
return rawText;
}

public boolean supported() {
return !UNSUPPORTED.equals(text);
return !UNSUPPORTED.equals(rawText);
}

@Override
public String normalizedQuery() {
if (UNSUPPORTED.equals(rawText)) {
// 特殊値を返さないようにする
// Queryのtextは外部から使用しないので例外でよい。これが発生したらバグ。
throw new IllegalArgumentException("BUG!!");
}
return normalizedQuery;
}

private static String normalizeSql(String query) {
String remaining = query;
while (true) {
String trimmed = remaining.stripLeading();
if (trimmed.startsWith("\uFEFF")) {
remaining = trimmed.substring(1);
continue;
}
if (trimmed.startsWith("--")) {
int newlineIndex = trimmed.indexOf('\n');
if (newlineIndex < 0) return "";
remaining = trimmed.substring(newlineIndex + 1);
continue;
}
if (trimmed.startsWith("/*")) {
int commentEndIndex = trimmed.indexOf("*/");
if (commentEndIndex < 0) return "";
remaining = trimmed.substring(commentEndIndex + 2);
continue;
}
if (trimmed.startsWith("(")) {
remaining = trimmed.substring(1);
continue;
}
return trimmed;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
/**
* SQL
*/
public record MyBatisStatement(MyBatisStatementId myBatisStatementId, Query query, SqlType sqlType) {
public record SqlStatement(SqlStatementId sqlStatementId, Query query, SqlType sqlType) {

public Tables tables() {
if (query.supported()) {
Table table = sqlType.extractTable(query.text(), myBatisStatementId);
Table table = sqlType.extractTable(query.normalizedQuery(), sqlStatementId);
return new Tables(table);
}
return new Tables(sqlType.unexpectedTable());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.dddjava.jig.domain.model.data.rdbaccess;

import java.util.Objects;

/**
* SQLステートメントID
*
* namespaceとidを.で連結したもの。
*
* TODO namespaceやidはMyBatisの用語なのでこの形のままとするかは一考の余地がある
*/
public record SqlStatementId(String value, String namespace, String id) {

public static SqlStatementId from(String value) {
var namespaceIdSeparateIndex = value.lastIndexOf('.');
if (namespaceIdSeparateIndex != -1) {
return new SqlStatementId(value, value.substring(0, namespaceIdSeparateIndex), value.substring(namespaceIdSeparateIndex + 1));
} else {
return new SqlStatementId(value, "<unknown namespace>", value);
}
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SqlStatementId that = (SqlStatementId) o;
return Objects.equals(value, that.value);
}

@Override
public int hashCode() {
return Objects.hash(value);
}

public String namespace() {
return namespace;
}

public String id() {
return id;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,34 @@
/**
* SQL一覧
*/
public record MyBatisStatements(List<MyBatisStatement> list) {
public record SqlStatements(List<SqlStatement> list) {

public static MyBatisStatements empty() {
return new MyBatisStatements(Collections.emptyList());
public static SqlStatements empty() {
return new SqlStatements(Collections.emptyList());
}

private Tables tables(SqlType sqlType) {
return list.stream()
.filter(myBatisStatement -> myBatisStatement.sqlType() == sqlType)
.map(MyBatisStatement::tables)
.filter(sqlStatement -> sqlStatement.sqlType() == sqlType)
.map(SqlStatement::tables)
.reduce(Tables::merge)
.orElse(Tables.nothing());
}

public Optional<MyBatisStatement> findById(MyBatisStatementId myBatisStatementId) {
public Optional<SqlStatement> findById(SqlStatementId sqlStatementId) {
return list.stream()
.filter(myBatisStatement -> myBatisStatement.myBatisStatementId().equals(myBatisStatementId))
.filter(sqlStatement -> sqlStatement.sqlStatementId().equals(sqlStatementId))
.findFirst();
}

/**
* 引数のメソッドに関連するステートメントに絞り込む
*/
public MyBatisStatements filterRelationOn(Predicate<MyBatisStatement> myBatisStatementPredicate) {
List<MyBatisStatement> myBatisStatements = list.stream()
.filter(myBatisStatementPredicate)
public SqlStatements filterRelationOn(Predicate<SqlStatement> sqlStatementPredicate) {
List<SqlStatement> sqlStatements = list.stream()
.filter(sqlStatementPredicate)
.toList();
return new MyBatisStatements(myBatisStatements);
return new SqlStatements(sqlStatements);
}

public boolean isEmpty() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
Expand Down Expand Up @@ -33,23 +35,33 @@ public enum SqlType {
* 現在は1テーブルのみ対応
* 複問い合わせやWITHなどは未対応
*/
public Table extractTable(String sql, MyBatisStatementId myBatisStatementId) {
public Table extractTable(String sql, SqlStatementId sqlStatementId) {
for (Pattern pattern : patterns) {
Matcher matcher = pattern.matcher(sql.replaceAll("\n", " "));
if (matcher.matches()) {
return new Table(matcher.group(1));
}
}

logger.warn("{} {} を {} としてテーブル名が解析できませんでした。このMapper由来の解析結果はドキュメントに出力されません。" +
"MyBatisの動的なSQLなどは完全に再現できません。JIGが認識しているSQL文=[{}]",
myBatisStatementId.namespace(),
myBatisStatementId.id(),
logger.warn("{} {} を {} としてテーブル名が解析できませんでした。テーブル名は「解析失敗」と表示されます。JIGが認識しているSQL文=[{}]",
sqlStatementId.namespace(),
sqlStatementId.id(),
this, sql);
return unexpectedTable();
}

public Table unexpectedTable() {
return new Table("(解析失敗)");
}

public static Optional<SqlType> inferSqlTypeFromQuery(Query query) {
String normalizedQuery = query.normalizedQuery().toLowerCase(Locale.ROOT);
if (normalizedQuery.startsWith("insert")) return Optional.of(INSERT);
if (normalizedQuery.startsWith("select")) return Optional.of(SELECT);
if (normalizedQuery.startsWith("update")) return Optional.of(UPDATE);
if (normalizedQuery.startsWith("delete")) return Optional.of(DELETE);

logger.info("SQLの種類がQuery文字列 [{}] から判別できませんでした。", query.rawText());
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package org.dddjava.jig.domain.model.information.outputs;

import org.dddjava.jig.domain.model.information.types.JigTypes;
import org.dddjava.jig.domain.model.data.types.JavaTypeDeclarationKind;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;

import static java.util.stream.Collectors.collectingAndThen;
Expand All @@ -25,13 +29,29 @@ public Gateways repositoryMethods() {
// FIXME これのテストがない
public static OutputImplementations from(JigTypes jigTypes, OutputAdapters outputAdapters) {
return outputAdapters.stream()
// output adapterの実装しているoutput portのgatewayを
.flatMap(outputAdapter -> outputAdapter.implementsPortStream(jigTypes)
// interfaceのRepository(Spring Data JDBCなど)は実装クラスが存在しないため、自身をoutput portとして扱う
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

元々のコメントが書きかけなのはおいといて。。。

この処理さらにアレになったなぁ

.flatMap(outputAdapter -> outputPorts(outputAdapter, jigTypes)
.flatMap(outputPort -> outputPort.gatewayStream()
// 実装しているinvocationが
.flatMap(gateway -> outputAdapter.resolveInvocation(gateway).stream()
.map(invocation -> new OutputImplementation(gateway, invocation, outputPort)))))
.collect(collectingAndThen(toList(), OutputImplementations::new));
.collect(collectingAndThen(toList(), outputImplementations ->
new OutputImplementations(outputImplementations.stream()
.collect(collectingAndThen(
java.util.stream.Collectors.toMap(
outputImplementation -> outputImplementation.outputPortGateway().jigMethodId().namespace()
+ "#" + outputImplementation.outputPortGateway().name(),
Function.identity(),
(existing, ignored) -> existing,
LinkedHashMap::new),
map -> List.copyOf(map.values()))))));
}

private static Stream<OutputPort> outputPorts(OutputAdapter outputAdapter, JigTypes jigTypes) {
if (outputAdapter.jigType().jigTypeHeader().javaTypeDeclarationKind() == JavaTypeDeclarationKind.INTERFACE) {
return Stream.of(new OutputPort(outputAdapter.jigType()));
}
return outputAdapter.implementsPortStream(jigTypes);
}

public Stream<OutputImplementation> stream() {
Expand Down
Loading