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.Data; |
|||
|
|||
@Data |
|||
@Builder |
|||
public class ColumnDoc { |
|||
public class ColumnMeta { |
|||
|
|||
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; |
|||
|
@ -1,43 +1,36 @@ |
|||
import com.databasir.core.doc.factory.DatabaseDocConfig; |
|||
import com.databasir.core.doc.factory.extension.mysql.MysqlTableTriggerDocFactory; |
|||
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 com.databasir.core.Databasir; |
|||
import com.databasir.core.meta.pojo.DatabaseMeta; |
|||
|
|||
import java.io.FileOutputStream; |
|||
import java.io.IOException; |
|||
import java.sql.Connection; |
|||
import java.sql.DriverManager; |
|||
import java.sql.SQLException; |
|||
import java.util.Properties; |
|||
|
|||
public class App { |
|||
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
|
|||
Class.forName("com.mysql.cj.jdbc.Driver"); |
|||
|
|||
Properties info = new Properties(); |
|||
info.put("user", "root"); |
|||
info.put("password", "123456"); |
|||
// this config is used by mysql
|
|||
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
|
|||
try (FileOutputStream out = new FileOutputStream("user.md")) { |
|||
RenderConfig renderConfig = new RenderConfig(); |
|||
renderConfig.setRenderTriggers(true); |
|||
Render.markdownRender(renderConfig).rendering(doc, out); |
|||
} catch (IOException e) { |
|||
throw new IllegalStateException(e); |
|||
} |
|||
String url = "jdbc:mysql://localhost:3306/patient?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true"; |
|||
return DriverManager.getConnection(url, info); |
|||
} |
|||
} |
|||
|
Loading…
Reference in new issue