vran
3 years ago
43 changed files with 636 additions and 477 deletions
@ -0,0 +1,46 @@ |
|||||
|
package com.databasir.core; |
||||
|
|
||||
|
import com.databasir.core.meta.pojo.DatabaseMeta; |
||||
|
import com.databasir.core.meta.repository.condition.Condition; |
||||
|
import com.databasir.core.render.Render; |
||||
|
import com.databasir.core.render.RenderConfig; |
||||
|
import lombok.Getter; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.io.OutputStream; |
||||
|
import java.sql.Connection; |
||||
|
import java.util.Optional; |
||||
|
|
||||
|
@RequiredArgsConstructor |
||||
|
@Getter |
||||
|
public class Databasir { |
||||
|
|
||||
|
private final DatabasirConfig config; |
||||
|
|
||||
|
public Optional<DatabaseMeta> get(Connection connection, String databaseName) { |
||||
|
Condition condition = Condition.builder() |
||||
|
.databaseName(databaseName) |
||||
|
.ignoreTableNameRegex(config.getIgnoreTableNameRegex()) |
||||
|
.ignoreTableColumnNameRegex(config.getIgnoreTableColumnNameRegex()) |
||||
|
.build(); |
||||
|
return config.getDatabaseMetaRepository().select(connection, condition); |
||||
|
} |
||||
|
|
||||
|
public void renderAsMarkdown(DatabaseMeta meta, OutputStream out) throws IOException { |
||||
|
renderAsMarkdown(new RenderConfig(), meta, out); |
||||
|
} |
||||
|
|
||||
|
public void renderAsMarkdown(RenderConfig config, DatabaseMeta meta, OutputStream stream) throws IOException { |
||||
|
Render.markdownRender(config).rendering(meta, stream); |
||||
|
} |
||||
|
|
||||
|
public static Databasir of() { |
||||
|
return of(new DatabasirConfig()); |
||||
|
} |
||||
|
|
||||
|
public static Databasir of(DatabasirConfig config) { |
||||
|
return new Databasir(config); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
package com.databasir.core; |
||||
|
|
||||
|
import com.databasir.core.meta.repository.*; |
||||
|
import com.databasir.core.meta.repository.impl.jdbc.*; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
import java.util.Collection; |
||||
|
import java.util.HashSet; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
public class DatabasirConfig { |
||||
|
|
||||
|
private IndexMetaRepository indexMetaRepository = new JdbcIndexMetaRepository(); |
||||
|
|
||||
|
private TriggerMetaRepository triggerMetaRepository = new JdbcTriggerMetaRepository(); |
||||
|
|
||||
|
private ColumnMetaRepository columnMetaRepository = new JdbcColumnMetaRepository(); |
||||
|
|
||||
|
private TableMetaRepository tableMetaRepository = |
||||
|
new JdbcTableMetaRepository(columnMetaRepository, indexMetaRepository, triggerMetaRepository); |
||||
|
|
||||
|
private DatabaseMetaRepository databaseMetaRepository = new JdbcDatabaseMetaRepository(tableMetaRepository); |
||||
|
|
||||
|
private Collection<String> ignoreTableNameRegex = new HashSet<>(); |
||||
|
|
||||
|
private Collection<String> ignoreTableColumnNameRegex = new HashSet<>(); |
||||
|
|
||||
|
public DatabasirConfig ignoreTable(String tableNameRegex) { |
||||
|
ignoreTableNameRegex.add(tableNameRegex); |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public DatabasirConfig ignoreColumn(String columnNameRegex) { |
||||
|
ignoreTableColumnNameRegex.add(columnNameRegex); |
||||
|
return this; |
||||
|
} |
||||
|
} |
@ -1,49 +0,0 @@ |
|||||
package com.databasir.core.doc.factory; |
|
||||
|
|
||||
import com.databasir.core.doc.factory.jdbc.*; |
|
||||
import lombok.Builder; |
|
||||
import lombok.Getter; |
|
||||
import lombok.NonNull; |
|
||||
|
|
||||
import java.sql.Connection; |
|
||||
import java.util.Collections; |
|
||||
import java.util.List; |
|
||||
import java.util.regex.Pattern; |
|
||||
|
|
||||
@Builder |
|
||||
@Getter |
|
||||
public class DatabaseDocConfig { |
|
||||
|
|
||||
@NonNull |
|
||||
private String databaseName; |
|
||||
|
|
||||
@NonNull |
|
||||
private Connection connection; |
|
||||
|
|
||||
@Builder.Default |
|
||||
private List<String> ignoreTableRegexes = Collections.emptyList(); |
|
||||
|
|
||||
@Builder.Default |
|
||||
private List<String> ignoreColumnRegexes = Collections.emptyList(); |
|
||||
|
|
||||
@Builder.Default |
|
||||
private TableDocFactory tableDocFactory = new JdbcTableDocFactory(); |
|
||||
|
|
||||
@Builder.Default |
|
||||
private TableIndexDocFactory tableIndexDocFactory = new JdbcTableIndexDocFactory(); |
|
||||
|
|
||||
@Builder.Default |
|
||||
private TableTriggerDocFactory tableTriggerDocFactory = new JdbcTableTriggerDocFactory(); |
|
||||
|
|
||||
@Builder.Default |
|
||||
private TableColumnDocFactory tableColumnDocFactory = new JdbcTableColumnDocFactory(); |
|
||||
|
|
||||
public boolean tableIsIgnored(String tableName) { |
|
||||
return ignoreTableRegexes.stream().anyMatch(regex -> Pattern.matches(regex, tableName)); |
|
||||
} |
|
||||
|
|
||||
public boolean columnIsIgnored(String column) { |
|
||||
return ignoreColumnRegexes.stream().anyMatch(regex -> Pattern.matches(regex, column)); |
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,11 +0,0 @@ |
|||||
package com.databasir.core.doc.factory; |
|
||||
|
|
||||
import com.databasir.core.doc.model.DatabaseDoc; |
|
||||
|
|
||||
import java.util.Optional; |
|
||||
|
|
||||
public interface DatabaseDocFactory extends Sortable<DatabaseDocFactory> { |
|
||||
|
|
||||
Optional<DatabaseDoc> create(DatabaseDocConfig configuration); |
|
||||
|
|
||||
} |
|
@ -1,16 +0,0 @@ |
|||||
package com.databasir.core.doc.factory; |
|
||||
|
|
||||
public interface Sortable<T extends Sortable<?>> extends Comparable<T> { |
|
||||
|
|
||||
/** |
|
||||
* @return priority, min -> max means low -> high |
|
||||
*/ |
|
||||
default int order() { |
|
||||
return Integer.MIN_VALUE; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
default int compareTo(T o) { |
|
||||
return Integer.compare(this.order(), o.order()); |
|
||||
} |
|
||||
} |
|
@ -1,12 +0,0 @@ |
|||||
package com.databasir.core.doc.factory; |
|
||||
|
|
||||
import com.databasir.core.doc.model.ColumnDoc; |
|
||||
|
|
||||
import java.sql.DatabaseMetaData; |
|
||||
import java.util.List; |
|
||||
|
|
||||
public interface TableColumnDocFactory extends Sortable<TableColumnDocFactory> { |
|
||||
|
|
||||
List<ColumnDoc> create(String tableName, DatabaseMetaData metaData, DatabaseDocConfig configuration); |
|
||||
|
|
||||
} |
|
@ -1,12 +0,0 @@ |
|||||
package com.databasir.core.doc.factory; |
|
||||
|
|
||||
import com.databasir.core.doc.model.TableDoc; |
|
||||
|
|
||||
import java.sql.DatabaseMetaData; |
|
||||
import java.util.List; |
|
||||
|
|
||||
public interface TableDocFactory extends Sortable<TableDocFactory> { |
|
||||
|
|
||||
List<TableDoc> create(DatabaseMetaData metaData, DatabaseDocConfig configuration); |
|
||||
|
|
||||
} |
|
@ -1,12 +0,0 @@ |
|||||
package com.databasir.core.doc.factory; |
|
||||
|
|
||||
import com.databasir.core.doc.model.IndexDoc; |
|
||||
|
|
||||
import java.sql.DatabaseMetaData; |
|
||||
import java.util.List; |
|
||||
|
|
||||
public interface TableIndexDocFactory extends Sortable<TableIndexDocFactory> { |
|
||||
|
|
||||
List<IndexDoc> create(String tableName, DatabaseMetaData metaData, DatabaseDocConfig configuration); |
|
||||
|
|
||||
} |
|
@ -1,11 +0,0 @@ |
|||||
package com.databasir.core.doc.factory; |
|
||||
|
|
||||
import com.databasir.core.doc.model.TriggerDoc; |
|
||||
|
|
||||
import java.sql.DatabaseMetaData; |
|
||||
import java.util.List; |
|
||||
|
|
||||
public interface TableTriggerDocFactory extends Sortable<TableTriggerDocFactory> { |
|
||||
|
|
||||
List<TriggerDoc> create(String tableName, DatabaseMetaData metaData, DatabaseDocConfig configuration); |
|
||||
} |
|
@ -1,45 +0,0 @@ |
|||||
package com.databasir.core.doc.factory.jdbc; |
|
||||
|
|
||||
import com.databasir.core.doc.factory.DatabaseDocConfig; |
|
||||
import com.databasir.core.doc.factory.*; |
|
||||
import com.databasir.core.doc.model.DatabaseDoc; |
|
||||
import com.databasir.core.doc.model.TableDoc; |
|
||||
|
|
||||
import java.sql.DatabaseMetaData; |
|
||||
import java.sql.ResultSet; |
|
||||
import java.sql.SQLException; |
|
||||
import java.util.List; |
|
||||
import java.util.Objects; |
|
||||
import java.util.Optional; |
|
||||
|
|
||||
public class JdbcDatabaseDocFactory implements DatabaseDocFactory { |
|
||||
|
|
||||
public static JdbcDatabaseDocFactory of() { |
|
||||
return new JdbcDatabaseDocFactory(); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public Optional<DatabaseDoc> create(DatabaseDocConfig configuration) { |
|
||||
try { |
|
||||
DatabaseMetaData metaData = configuration.getConnection().getMetaData(); |
|
||||
ResultSet catalogs = metaData.getCatalogs(); |
|
||||
while (catalogs.next()) { |
|
||||
String catalogName = catalogs.getString("TABLE_CAT"); |
|
||||
if (Objects.equals(configuration.getDatabaseName(), catalogName)) { |
|
||||
TableDocFactory tableDocFactory = configuration.getTableDocFactory(); |
|
||||
List<TableDoc> tableDocs = tableDocFactory.create(metaData, configuration); |
|
||||
DatabaseDoc databaseDoc = DatabaseDoc.builder() |
|
||||
.productName(metaData.getDatabaseProductName()) |
|
||||
.productVersion(metaData.getDatabaseProductVersion()) |
|
||||
.databaseName(catalogName) |
|
||||
.tables(tableDocs) |
|
||||
.build(); |
|
||||
return Optional.of(databaseDoc); |
|
||||
} |
|
||||
} |
|
||||
return Optional.empty(); |
|
||||
} catch (SQLException e) { |
|
||||
throw new IllegalStateException(e); |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,59 +0,0 @@ |
|||||
package com.databasir.core.doc.factory.jdbc; |
|
||||
|
|
||||
import com.databasir.core.doc.factory.DatabaseDocConfig; |
|
||||
import com.databasir.core.doc.factory.*; |
|
||||
import com.databasir.core.doc.model.TableDoc; |
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
|
|
||||
import java.sql.DatabaseMetaData; |
|
||||
import java.sql.ResultSet; |
|
||||
import java.sql.SQLException; |
|
||||
import java.util.ArrayList; |
|
||||
import java.util.List; |
|
||||
|
|
||||
@Slf4j |
|
||||
public class JdbcTableDocFactory implements TableDocFactory { |
|
||||
|
|
||||
@Override |
|
||||
public List<TableDoc> create(DatabaseMetaData metaData, |
|
||||
DatabaseDocConfig configuration) { |
|
||||
try { |
|
||||
return doCreateTableDoc(metaData, configuration); |
|
||||
} catch (SQLException e) { |
|
||||
throw new IllegalStateException(e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private List<TableDoc> doCreateTableDoc(DatabaseMetaData metaData, |
|
||||
DatabaseDocConfig configuration) throws SQLException { |
|
||||
List<TableDoc> tableDocs = new ArrayList<>(); |
|
||||
if (metaData == null) { |
|
||||
return tableDocs; |
|
||||
} |
|
||||
|
|
||||
String databaseName = configuration.getDatabaseName(); |
|
||||
ResultSet tablesResult = metaData.getTables(databaseName, null, null, null); |
|
||||
while (tablesResult.next()) { |
|
||||
String tableName = tablesResult.getString("TABLE_NAME"); |
|
||||
if (configuration.tableIsIgnored(tableName)) { |
|
||||
if (log.isWarnEnabled()) { |
|
||||
log.warn("ignore table: " + configuration.getDatabaseName() + "." + tableName); |
|
||||
} |
|
||||
} else { |
|
||||
String tableType = tablesResult.getString("TABLE_TYPE"); |
|
||||
String tableComment = tablesResult.getString("REMARKS"); |
|
||||
TableDoc tableDoc = TableDoc.builder() |
|
||||
.tableName(tableName) |
|
||||
.tableType(tableType) |
|
||||
.tableComment(tableComment) |
|
||||
.columns(configuration.getTableColumnDocFactory().create(tableName, metaData, configuration)) |
|
||||
.indexes(configuration.getTableIndexDocFactory().create(tableName, metaData, configuration)) |
|
||||
.triggers(configuration.getTableTriggerDocFactory().create(tableName, metaData, configuration)) |
|
||||
.build(); |
|
||||
tableDocs.add(tableDoc); |
|
||||
} |
|
||||
} |
|
||||
return tableDocs; |
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,64 +0,0 @@ |
|||||
package com.databasir.core.doc.factory.jdbc; |
|
||||
|
|
||||
import com.databasir.core.doc.factory.DatabaseDocConfig; |
|
||||
import com.databasir.core.doc.factory.TableIndexDocFactory; |
|
||||
import com.databasir.core.doc.model.IndexDoc; |
|
||||
import lombok.extern.slf4j.Slf4j; |
|
||||
|
|
||||
import java.sql.DatabaseMetaData; |
|
||||
import java.sql.ResultSet; |
|
||||
import java.sql.SQLException; |
|
||||
import java.util.*; |
|
||||
|
|
||||
@Slf4j |
|
||||
public class JdbcTableIndexDocFactory implements TableIndexDocFactory { |
|
||||
|
|
||||
@Override |
|
||||
public List<IndexDoc> create(String tableName, DatabaseMetaData metaData, DatabaseDocConfig configuration) { |
|
||||
try { |
|
||||
return doCreateIndexDocs(tableName, metaData, configuration); |
|
||||
} catch (SQLException e) { |
|
||||
throw new IllegalStateException(e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private List<IndexDoc> doCreateIndexDocs(String tableName, |
|
||||
DatabaseMetaData metaData, |
|
||||
DatabaseDocConfig configuration) |
|
||||
throws SQLException { |
|
||||
List<IndexDoc> indexDocs = new ArrayList<>(); |
|
||||
String databaseName = configuration.getDatabaseName(); |
|
||||
if (tableName == null || metaData == null) { |
|
||||
return indexDocs; |
|
||||
} |
|
||||
ResultSet indexResults; |
|
||||
try { |
|
||||
indexResults = metaData.getIndexInfo(databaseName, null, tableName, false, false); |
|
||||
} catch (SQLException e) { |
|
||||
log.warn("warn: ignore " + databaseName + "." + tableName); |
|
||||
return indexDocs; |
|
||||
} |
|
||||
|
|
||||
Map<String, IndexDoc> docsGroupByName = new HashMap<>(); |
|
||||
while (indexResults.next()) { |
|
||||
Boolean nonUnique = indexResults.getBoolean("NON_UNIQUE"); |
|
||||
String indexName = indexResults.getString("INDEX_NAME"); |
|
||||
String columnName = indexResults.getString("COLUMN_NAME"); |
|
||||
if (docsGroupByName.containsKey(indexName)) { |
|
||||
docsGroupByName.get(indexName).getColumnNames().add(columnName); |
|
||||
} else { |
|
||||
List<String> columns = new ArrayList<>(); |
|
||||
columns.add(columnName); |
|
||||
IndexDoc columnDoc = IndexDoc.builder() |
|
||||
.indexName(indexName) |
|
||||
.columnNames(columns) |
|
||||
.isPrimaryKey(Objects.equals("PRIMARY", indexName)) |
|
||||
.isUniqueKey(Objects.equals(nonUnique, false)) |
|
||||
.build(); |
|
||||
docsGroupByName.put(indexName, columnDoc); |
|
||||
} |
|
||||
} |
|
||||
return new ArrayList<>(docsGroupByName.values()); |
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,18 +0,0 @@ |
|||||
package com.databasir.core.doc.factory.jdbc; |
|
||||
|
|
||||
import com.databasir.core.doc.factory.DatabaseDocConfig; |
|
||||
import com.databasir.core.doc.factory.TableTriggerDocFactory; |
|
||||
import com.databasir.core.doc.model.TriggerDoc; |
|
||||
|
|
||||
import java.sql.DatabaseMetaData; |
|
||||
import java.util.Collections; |
|
||||
import java.util.List; |
|
||||
|
|
||||
public class JdbcTableTriggerDocFactory implements TableTriggerDocFactory { |
|
||||
|
|
||||
@Override |
|
||||
public List<TriggerDoc> create(String tableName, DatabaseMetaData metaData, DatabaseDocConfig configuration) { |
|
||||
// note: jdbc not support get triggers
|
|
||||
return Collections.emptyList(); |
|
||||
} |
|
||||
} |
|
@ -1,16 +0,0 @@ |
|||||
package com.databasir.core.doc.render; |
|
||||
|
|
||||
import com.databasir.core.doc.model.DatabaseDoc; |
|
||||
import com.databasir.core.doc.render.markdown.MarkdownRender; |
|
||||
|
|
||||
import java.io.IOException; |
|
||||
import java.io.OutputStream; |
|
||||
|
|
||||
public interface Render { |
|
||||
|
|
||||
void rendering(DatabaseDoc doc, OutputStream outputStream) throws IOException; |
|
||||
|
|
||||
static Render markdownRender(RenderConfig configuration) { |
|
||||
return MarkdownRender.of(configuration); |
|
||||
} |
|
||||
} |
|
@ -1,11 +1,11 @@ |
|||||
package com.databasir.core.doc.model; |
package com.databasir.core.meta.pojo; |
||||
|
|
||||
import lombok.Builder; |
import lombok.Builder; |
||||
import lombok.Data; |
import lombok.Data; |
||||
|
|
||||
@Data |
@Data |
||||
@Builder |
@Builder |
||||
public class ColumnDoc { |
public class ColumnMeta { |
||||
|
|
||||
private String name; |
private String name; |
||||
|
|
@ -0,0 +1,80 @@ |
|||||
|
package com.databasir.core.meta.provider; |
||||
|
|
||||
|
import java.util.Optional; |
||||
|
|
||||
|
/** |
||||
|
* TODO use to extension repository |
||||
|
*/ |
||||
|
public interface SqlProvider { |
||||
|
|
||||
|
Default DEFAULT = new Default(); |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* generate sql to select database information, should return the follow columns |
||||
|
* </p> |
||||
|
* |
||||
|
* <table> |
||||
|
* <tr> |
||||
|
* <th> column name </th> |
||||
|
* <th> column type </th> |
||||
|
* <th> description </th> |
||||
|
* <th> nullable </th> |
||||
|
* </tr> |
||||
|
* <tr> |
||||
|
* <td> TABLE_CAT </td> |
||||
|
* <td> String </td> |
||||
|
* <td> catalog name </td> |
||||
|
* <td> NO </td> |
||||
|
* </tr> |
||||
|
* </table> |
||||
|
* <br> |
||||
|
* |
||||
|
* @param databaseName |
||||
|
* @return |
||||
|
*/ |
||||
|
default Optional<String> databaseMetaSql(String databaseName) { |
||||
|
return Optional.empty(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* generate sql to select table information, should return the follow columns |
||||
|
* <table> |
||||
|
* <tr> |
||||
|
* <th> column name </th> |
||||
|
* <th> column type </th> |
||||
|
* <th> description </th> |
||||
|
* <th> nullable </th> |
||||
|
* </tr> |
||||
|
* <tr> |
||||
|
* <td> TABLE_CAT </td> |
||||
|
* <td> String </td> |
||||
|
* <td> catalog name </td> |
||||
|
* <td> NO </td> |
||||
|
* </tr> |
||||
|
* </table> |
||||
|
* |
||||
|
* @param databaseName |
||||
|
* @param tableName |
||||
|
* @return |
||||
|
*/ |
||||
|
default Optional<String> tableMetaSql(String databaseName, String tableName) { |
||||
|
return Optional.empty(); |
||||
|
} |
||||
|
|
||||
|
default Optional<String> tableColumnMetaSql(String databaseName, String tableName) { |
||||
|
return Optional.empty(); |
||||
|
} |
||||
|
|
||||
|
default Optional<String> tableIndexMetaSql(String databaseName, String tableName) { |
||||
|
return Optional.empty(); |
||||
|
} |
||||
|
|
||||
|
default Optional<String> tableTriggerMetaSql(String databaseName, String tableName) { |
||||
|
return Optional.empty(); |
||||
|
} |
||||
|
|
||||
|
class Default implements SqlProvider { |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
package com.databasir.core.meta.repository; |
||||
|
|
||||
|
import com.databasir.core.meta.pojo.ColumnMeta; |
||||
|
import com.databasir.core.meta.repository.condition.TableCondition; |
||||
|
|
||||
|
import java.sql.Connection; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface ColumnMetaRepository { |
||||
|
|
||||
|
List<ColumnMeta> selectColumns(Connection connection, TableCondition condition); |
||||
|
|
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
package com.databasir.core.meta.repository; |
||||
|
|
||||
|
import com.databasir.core.meta.pojo.DatabaseMeta; |
||||
|
import com.databasir.core.meta.repository.condition.Condition; |
||||
|
|
||||
|
import java.sql.Connection; |
||||
|
import java.util.Optional; |
||||
|
|
||||
|
public interface DatabaseMetaRepository { |
||||
|
|
||||
|
Optional<DatabaseMeta> select(Connection connection, Condition condition); |
||||
|
|
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
package com.databasir.core.meta.repository; |
||||
|
|
||||
|
import com.databasir.core.meta.pojo.IndexMeta; |
||||
|
import com.databasir.core.meta.repository.condition.TableCondition; |
||||
|
|
||||
|
import java.sql.Connection; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface IndexMetaRepository { |
||||
|
|
||||
|
List<IndexMeta> selectIndexes(Connection connection, TableCondition condition); |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
package com.databasir.core.meta.repository; |
||||
|
|
||||
|
import com.databasir.core.meta.pojo.TableMeta; |
||||
|
import com.databasir.core.meta.repository.condition.Condition; |
||||
|
|
||||
|
import java.sql.Connection; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface TableMetaRepository { |
||||
|
|
||||
|
List<TableMeta> selectTables(Connection connection, Condition condition); |
||||
|
|
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
package com.databasir.core.meta.repository; |
||||
|
|
||||
|
import com.databasir.core.meta.pojo.TriggerMeta; |
||||
|
import com.databasir.core.meta.repository.condition.TableCondition; |
||||
|
|
||||
|
import java.sql.Connection; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface TriggerMetaRepository { |
||||
|
|
||||
|
List<TriggerMeta> selectTriggers(Connection connection, TableCondition condition); |
||||
|
|
||||
|
} |
@ -0,0 +1,32 @@ |
|||||
|
package com.databasir.core.meta.repository.condition; |
||||
|
|
||||
|
import lombok.Builder; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NonNull; |
||||
|
import lombok.experimental.SuperBuilder; |
||||
|
|
||||
|
import java.util.Collection; |
||||
|
import java.util.Collections; |
||||
|
import java.util.regex.Pattern; |
||||
|
|
||||
|
@SuperBuilder |
||||
|
@Getter |
||||
|
public class Condition { |
||||
|
|
||||
|
@NonNull |
||||
|
private String databaseName; |
||||
|
|
||||
|
@Builder.Default |
||||
|
private Collection<String> ignoreTableNameRegex = Collections.emptyList(); |
||||
|
|
||||
|
@Builder.Default |
||||
|
private Collection<String> ignoreTableColumnNameRegex = Collections.emptyList(); |
||||
|
|
||||
|
public boolean tableIsIgnored(String tableName) { |
||||
|
return ignoreTableNameRegex.stream().anyMatch(regex -> Pattern.matches(regex, tableName)); |
||||
|
} |
||||
|
|
||||
|
public boolean columnIsIgnored(String column) { |
||||
|
return ignoreTableColumnNameRegex.stream().anyMatch(regex -> Pattern.matches(regex, column)); |
||||
|
} |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
package com.databasir.core.meta.repository.condition; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
import lombok.NonNull; |
||||
|
import lombok.experimental.SuperBuilder; |
||||
|
|
||||
|
@SuperBuilder |
||||
|
@Getter |
||||
|
public class TableCondition extends Condition { |
||||
|
|
||||
|
@NonNull |
||||
|
private String tableName; |
||||
|
|
||||
|
public static TableCondition of(Condition condition, String tableName) { |
||||
|
return TableCondition.builder() |
||||
|
.databaseName(condition.getDatabaseName()) |
||||
|
.tableName(tableName) |
||||
|
.ignoreTableNameRegex(condition.getIgnoreTableNameRegex()) |
||||
|
.ignoreTableColumnNameRegex(condition.getIgnoreTableColumnNameRegex()) |
||||
|
.build(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
package com.databasir.core.meta.repository.impl.jdbc; |
||||
|
|
||||
|
import com.databasir.core.meta.pojo.DatabaseMeta; |
||||
|
import com.databasir.core.meta.pojo.TableMeta; |
||||
|
import com.databasir.core.meta.repository.DatabaseMetaRepository; |
||||
|
import com.databasir.core.meta.repository.TableMetaRepository; |
||||
|
import com.databasir.core.meta.repository.condition.Condition; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
|
||||
|
import java.sql.Connection; |
||||
|
import java.sql.DatabaseMetaData; |
||||
|
import java.sql.ResultSet; |
||||
|
import java.sql.SQLException; |
||||
|
import java.util.List; |
||||
|
import java.util.Objects; |
||||
|
import java.util.Optional; |
||||
|
|
||||
|
@RequiredArgsConstructor |
||||
|
public class JdbcDatabaseMetaRepository implements DatabaseMetaRepository { |
||||
|
|
||||
|
private final TableMetaRepository tableMetaRepository; |
||||
|
|
||||
|
@Override |
||||
|
public Optional<DatabaseMeta> select(Connection connection, Condition condition) { |
||||
|
try { |
||||
|
DatabaseMetaData metaData = connection.getMetaData(); |
||||
|
ResultSet catalogs = metaData.getCatalogs(); |
||||
|
while (catalogs.next()) { |
||||
|
String catalogName = catalogs.getString("TABLE_CAT"); |
||||
|
if (Objects.equals(condition.getDatabaseName(), catalogName)) { |
||||
|
List<TableMeta> tableDocs = tableMetaRepository.selectTables(connection, condition); |
||||
|
DatabaseMeta meta = DatabaseMeta.builder() |
||||
|
.productName(metaData.getDatabaseProductName()) |
||||
|
.productVersion(metaData.getDatabaseProductVersion()) |
||||
|
.databaseName(catalogName) |
||||
|
.tables(tableDocs) |
||||
|
.build(); |
||||
|
return Optional.of(meta); |
||||
|
} |
||||
|
} |
||||
|
return Optional.empty(); |
||||
|
} catch (SQLException e) { |
||||
|
throw new IllegalStateException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,59 @@ |
|||||
|
package com.databasir.core.meta.repository.impl.jdbc; |
||||
|
|
||||
|
import com.databasir.core.meta.pojo.IndexMeta; |
||||
|
import com.databasir.core.meta.repository.IndexMetaRepository; |
||||
|
import com.databasir.core.meta.repository.condition.TableCondition; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
import java.sql.Connection; |
||||
|
import java.sql.ResultSet; |
||||
|
import java.sql.SQLException; |
||||
|
import java.util.*; |
||||
|
|
||||
|
@Slf4j |
||||
|
public class JdbcIndexMetaRepository implements IndexMetaRepository { |
||||
|
@Override |
||||
|
public List<IndexMeta> selectIndexes(Connection connection, TableCondition condition) { |
||||
|
try { |
||||
|
return doCreateIndexDocs(connection, condition); |
||||
|
} catch (SQLException e) { |
||||
|
throw new IllegalStateException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private List<IndexMeta> doCreateIndexDocs(Connection connection, TableCondition condition) |
||||
|
throws SQLException { |
||||
|
String databaseName = condition.getDatabaseName(); |
||||
|
String tableName = condition.getTableName(); |
||||
|
List<IndexMeta> indexMetas = new ArrayList<>(); |
||||
|
ResultSet indexResults; |
||||
|
try { |
||||
|
indexResults = connection.getMetaData().getIndexInfo(databaseName, null, tableName, false, false); |
||||
|
} catch (SQLException e) { |
||||
|
log.warn("warn: ignore " + databaseName + "." + tableName); |
||||
|
return indexMetas; |
||||
|
} |
||||
|
|
||||
|
Map<String, IndexMeta> pojoGroupByName = new HashMap<>(); |
||||
|
while (indexResults.next()) { |
||||
|
Boolean nonUnique = indexResults.getBoolean("NON_UNIQUE"); |
||||
|
String indexName = indexResults.getString("INDEX_NAME"); |
||||
|
String columnName = indexResults.getString("COLUMN_NAME"); |
||||
|
if (pojoGroupByName.containsKey(indexName)) { |
||||
|
pojoGroupByName.get(indexName).getColumnNames().add(columnName); |
||||
|
} else { |
||||
|
List<String> columns = new ArrayList<>(); |
||||
|
columns.add(columnName); |
||||
|
IndexMeta indexMeta = IndexMeta.builder() |
||||
|
.indexName(indexName) |
||||
|
.columnNames(columns) |
||||
|
.isPrimaryKey(Objects.equals("PRIMARY", indexName)) |
||||
|
.isUniqueKey(Objects.equals(nonUnique, false)) |
||||
|
.build(); |
||||
|
pojoGroupByName.put(indexName, indexMeta); |
||||
|
} |
||||
|
} |
||||
|
return new ArrayList<>(pojoGroupByName.values()); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
package com.databasir.core.meta.repository.impl.jdbc; |
||||
|
|
||||
|
import com.databasir.core.meta.pojo.TableMeta; |
||||
|
import com.databasir.core.meta.repository.ColumnMetaRepository; |
||||
|
import com.databasir.core.meta.repository.IndexMetaRepository; |
||||
|
import com.databasir.core.meta.repository.TableMetaRepository; |
||||
|
import com.databasir.core.meta.repository.TriggerMetaRepository; |
||||
|
import com.databasir.core.meta.repository.condition.Condition; |
||||
|
import com.databasir.core.meta.repository.condition.TableCondition; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
import java.sql.Connection; |
||||
|
import java.sql.ResultSet; |
||||
|
import java.sql.SQLException; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@RequiredArgsConstructor |
||||
|
@Slf4j |
||||
|
public class JdbcTableMetaRepository implements TableMetaRepository { |
||||
|
|
||||
|
private final ColumnMetaRepository columnMetaRepository; |
||||
|
|
||||
|
private final IndexMetaRepository indexMetaRepository; |
||||
|
|
||||
|
private final TriggerMetaRepository triggerMetaRepository; |
||||
|
|
||||
|
@Override |
||||
|
public List<TableMeta> selectTables(Connection connection, Condition condition) { |
||||
|
try { |
||||
|
return doSelect(connection, condition); |
||||
|
} catch (SQLException e) { |
||||
|
throw new IllegalStateException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private List<TableMeta> doSelect(Connection connection, Condition condition) throws SQLException { |
||||
|
List<TableMeta> tableMetas = new ArrayList<>(); |
||||
|
String databaseName = condition.getDatabaseName(); |
||||
|
ResultSet tablesResult = connection.getMetaData().getTables(databaseName, null, null, null); |
||||
|
while (tablesResult.next()) { |
||||
|
String tableName = tablesResult.getString("TABLE_NAME"); |
||||
|
if (condition.tableIsIgnored(tableName)) { |
||||
|
if (log.isWarnEnabled()) { |
||||
|
log.warn("ignored table: " + databaseName + "." + tableName); |
||||
|
} |
||||
|
} else { |
||||
|
String tableType = tablesResult.getString("TABLE_TYPE"); |
||||
|
String tableComment = tablesResult.getString("REMARKS"); |
||||
|
TableCondition tableCondition = TableCondition.of(condition, tableName); |
||||
|
TableMeta tableMeta = TableMeta.builder() |
||||
|
.tableName(tableName) |
||||
|
.tableType(tableType) |
||||
|
.tableComment(tableComment) |
||||
|
.columns(columnMetaRepository.selectColumns(connection, tableCondition)) |
||||
|
.indexes(indexMetaRepository.selectIndexes(connection, tableCondition)) |
||||
|
.triggers(triggerMetaRepository.selectTriggers(connection, tableCondition)) |
||||
|
.build(); |
||||
|
tableMetas.add(tableMeta); |
||||
|
} |
||||
|
} |
||||
|
return tableMetas; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
package com.databasir.core.meta.repository.impl.jdbc; |
||||
|
|
||||
|
import com.databasir.core.meta.pojo.TriggerMeta; |
||||
|
import com.databasir.core.meta.repository.TriggerMetaRepository; |
||||
|
import com.databasir.core.meta.repository.condition.TableCondition; |
||||
|
|
||||
|
import java.sql.Connection; |
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public class JdbcTriggerMetaRepository implements TriggerMetaRepository { |
||||
|
|
||||
|
@Override |
||||
|
public List<TriggerMeta> selectTriggers(Connection connection, TableCondition condition) { |
||||
|
// note: jdbc not support get triggers
|
||||
|
return Collections.emptyList(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package com.databasir.core.render; |
||||
|
|
||||
|
import com.databasir.core.meta.pojo.DatabaseMeta; |
||||
|
import com.databasir.core.render.markdown.MarkdownRender; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.io.OutputStream; |
||||
|
|
||||
|
public interface Render { |
||||
|
|
||||
|
void rendering(DatabaseMeta meta, OutputStream outputStream) throws IOException; |
||||
|
|
||||
|
static Render markdownRender(RenderConfig configuration) { |
||||
|
return MarkdownRender.of(configuration); |
||||
|
} |
||||
|
} |
@ -1,4 +1,4 @@ |
|||||
package com.databasir.core.doc.render.markdown; |
package com.databasir.core.render.markdown; |
||||
|
|
||||
import java.util.List; |
import java.util.List; |
||||
|
|
@ -1,43 +1,36 @@ |
|||||
import com.databasir.core.doc.factory.DatabaseDocConfig; |
import com.databasir.core.Databasir; |
||||
import com.databasir.core.doc.factory.extension.mysql.MysqlTableTriggerDocFactory; |
import com.databasir.core.meta.pojo.DatabaseMeta; |
||||
import com.databasir.core.doc.factory.jdbc.JdbcDatabaseDocFactory; |
|
||||
import com.databasir.core.doc.model.DatabaseDoc; |
|
||||
import com.databasir.core.doc.render.Render; |
|
||||
import com.databasir.core.doc.render.RenderConfig; |
|
||||
|
|
||||
import java.io.FileOutputStream; |
import java.io.FileOutputStream; |
||||
import java.io.IOException; |
import java.io.IOException; |
||||
|
import java.sql.Connection; |
||||
import java.sql.DriverManager; |
import java.sql.DriverManager; |
||||
import java.sql.SQLException; |
import java.sql.SQLException; |
||||
import java.util.Properties; |
import java.util.Properties; |
||||
|
|
||||
public class App { |
public class App { |
||||
public static void main(String[] args) throws SQLException, ClassNotFoundException { |
public static void main(String[] args) throws SQLException, ClassNotFoundException { |
||||
|
try (FileOutputStream out = new FileOutputStream("user.md")) { |
||||
|
Connection connection = getJdbcConnection(); |
||||
|
Databasir databasir = Databasir.of(); |
||||
|
DatabaseMeta doc = databasir.get(connection, "user").orElseThrow(); |
||||
|
databasir.renderAsMarkdown(doc, out); |
||||
|
} catch (IOException e) { |
||||
|
throw new IllegalStateException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static Connection getJdbcConnection() throws SQLException, ClassNotFoundException { |
||||
// get database connection
|
// get database connection
|
||||
Class.forName("com.mysql.cj.jdbc.Driver"); |
Class.forName("com.mysql.cj.jdbc.Driver"); |
||||
|
|
||||
Properties info = new Properties(); |
Properties info = new Properties(); |
||||
info.put("user", "root"); |
info.put("user", "root"); |
||||
info.put("password", "123456"); |
info.put("password", "123456"); |
||||
// this config is used by mysql
|
// this config is used by mysql
|
||||
info.put("useInformationSchema", "true"); |
info.put("useInformationSchema", "true"); |
||||
String url = "jdbc:mysql://localhost:3306/patient?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true"; |
|
||||
var connection = DriverManager.getConnection(url, info); |
|
||||
|
|
||||
// generate doc model
|
|
||||
var config = DatabaseDocConfig.builder() |
|
||||
.databaseName("user") |
|
||||
.connection(connection) |
|
||||
.tableTriggerDocFactory(new MysqlTableTriggerDocFactory()) |
|
||||
.build(); |
|
||||
DatabaseDoc doc = JdbcDatabaseDocFactory.of().create(config).orElseThrow(); |
|
||||
|
|
||||
// render as markdown
|
String url = "jdbc:mysql://localhost:3306/patient?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true"; |
||||
try (FileOutputStream out = new FileOutputStream("user.md")) { |
return DriverManager.getConnection(url, info); |
||||
RenderConfig renderConfig = new RenderConfig(); |
|
||||
renderConfig.setRenderTriggers(true); |
|
||||
Render.markdownRender(renderConfig).rendering(doc, out); |
|
||||
} catch (IOException e) { |
|
||||
throw new IllegalStateException(e); |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
Loading…
Reference in new issue