添加初始代码

This commit is contained in:
datagear 2018-09-12 20:16:11 +08:00
parent 1a673148eb
commit d15c997891
500 changed files with 144345 additions and 19 deletions

23
.gitignore vendored
View File

@ -1,23 +1,8 @@
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
**/.settings/
**/.classpath
**/.project
*/target/
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

1
README.md Normal file
View File

@ -0,0 +1 @@
# Data Gear

1
TODO.txt Normal file
View File

@ -0,0 +1 @@
[web]添加内置常用数据库驱动;

View File

@ -0,0 +1,29 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.datagear</groupId>
<artifactId>datagear</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>datagear-connection</artifactId>
<name>datagear-connection</name>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
/**
* 抽象{@linkplain DriverChecker}
*
* @author datagear@163.com
*
*/
public abstract class AbstractDriverChecker implements DriverChecker
{
public AbstractDriverChecker()
{
super();
}
@Override
public boolean check(Driver driver, ConnectionOption connectionOption, boolean ignoreAcceptsURLCheck)
throws Throwable
{
if (!ignoreAcceptsURLCheck)
{
if (!acceptsURL(driver, connectionOption.getUrl()))
return false;
}
return checkConnection(driver, connectionOption);
}
/**
* 校验{@linkplain Driver}连接是否支持给定{@linkplain ConnectionOption}
* <p>
* {@linkplain Driver#acceptsURL(String)}无需在此方法中校验
* </p>
*
* @param driver
* @param connectionOption
* @return
* @throws Throwable
*/
protected abstract boolean checkConnection(Driver driver, ConnectionOption connectionOption) throws Throwable;
/**
* 获取连接
*
* @param driver
* @param connectionOption
* @return
* @throws Throwable
*/
protected Connection getConnection(Driver driver, ConnectionOption connectionOption) throws Throwable
{
return driver.connect(connectionOption.getUrl(), connectionOption.getProperties());
}
/**
* 关闭连接
*
* @param cn
*/
protected void closeConnection(Connection cn)
{
JdbcUtil.closeConnection(cn);
}
/**
* 判断{@linkplain Driver}是否能支持给定URL
*
* @param driver
* @param url
* @return
* @throws SQLException
*/
protected boolean acceptsURL(Driver driver, String url) throws SQLException
{
return driver.acceptsURL(url);
}
}

View File

@ -0,0 +1,768 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.sql.Driver;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 基于文件的{@linkplain DriverEntityManager}
*
* @author datagear@163.com
*
*/
public abstract class AbstractFileDriverEntityManager implements DriverEntityManager
{
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractFileDriverEntityManager.class);
public static final String DEFAULT_DRIVER_ENTITY_FILE_ENCODING = "UTF-8";
private File rootDirectory;
private String driverEntityInfoFileName;
private String driverEntityFileEncoding = DEFAULT_DRIVER_ENTITY_FILE_ENCODING;
private Map<String, PathDriverFactoryInfo> pathDriverFactoryInfoMap = new HashMap<String, PathDriverFactoryInfo>();
private List<DriverEntity> driverEntities = null;
private File driverEntityInfoFile = null;
private long driverEntityInfoFileLastModified = -1;
public AbstractFileDriverEntityManager()
{
super();
}
public AbstractFileDriverEntityManager(String rootDirectory, String driverEntityInfoFileName)
{
this(new File(rootDirectory), driverEntityInfoFileName);
}
public AbstractFileDriverEntityManager(File rootDirectory, String driverEntityInfoFileName)
{
super();
this.rootDirectory = rootDirectory;
this.driverEntityInfoFileName = driverEntityInfoFileName;
}
public File getRootDirectory()
{
return rootDirectory;
}
public void setRootDirectory(File rootDirectory)
{
this.rootDirectory = rootDirectory;
}
public String getDriverEntityInfoFileName()
{
return driverEntityInfoFileName;
}
public void setDriverEntityInfoFileName(String driverEntityInfoFileName)
{
this.driverEntityInfoFileName = driverEntityInfoFileName;
}
public String getDriverEntityFileEncoding()
{
return driverEntityFileEncoding;
}
public void setDriverEntityFileEncoding(String driverEntityFileEncoding)
{
this.driverEntityFileEncoding = driverEntityFileEncoding;
}
public File getDriverEntityInfoFile()
{
return new File(this.rootDirectory, this.driverEntityInfoFileName);
}
/**
* 初始化
*
* @throws DriverEntityManagerException
*/
public void init() throws DriverEntityManagerException
{
if (!this.rootDirectory.exists())
this.rootDirectory.mkdirs();
this.driverEntityInfoFile = getDriverEntityInfoFile();
read();
}
@Override
public synchronized void add(DriverEntity... driverEntities) throws DriverEntityManagerException
{
reloadDriverEntityFileIfModified();
for (DriverEntity driverEntity : driverEntities)
checkInput(driverEntity);
for (DriverEntity driverEntity : driverEntities)
{
removeExists(this.driverEntities, driverEntity.getId());
this.driverEntities.add(driverEntity);
}
write();
}
@Override
public boolean[] update(DriverEntity... driverEntities) throws DriverEntityManagerException
{
reloadDriverEntityFileIfModified();
for (DriverEntity driverEntity : driverEntities)
checkInput(driverEntity);
boolean[] updated = new boolean[driverEntities.length];
for (int i = 0; i < driverEntities.length; i++)
{
DriverEntity driverEntity = driverEntities[i];
int index = findDriverEntityIndex(this.driverEntities, driverEntity.getId());
if (index >= 0)
{
this.driverEntities.set(index, driverEntity);
updated[i] = true;
}
else
updated[i] = false;
}
write();
return updated;
}
@Override
public synchronized DriverEntity get(String path) throws DriverEntityManagerException
{
reloadDriverEntityFileIfModified();
int index = findDriverEntityIndex(this.driverEntities, path);
return (index < 0 ? null : this.driverEntities.get(index));
}
@Override
public synchronized void delete(String... paths) throws DriverEntityManagerException
{
int removeCount = 0;
for (int i = 0; i < paths.length; i++)
{
removeCount += removeExists(driverEntities, paths[i]);
deleteDriverLibraryDirectory(paths[i]);
}
if (removeCount > 0)
write();
}
@Override
public synchronized List<DriverEntity> getAll() throws DriverEntityManagerException
{
reloadDriverEntityFileIfModified();
return new ArrayList<DriverEntity>(this.driverEntities);
}
@Override
public long getLastModified() throws DriverEntityManagerException
{
return this.driverEntityInfoFileLastModified;
}
@Override
public synchronized void addDriverLibrary(DriverEntity driverEntity, String libraryName, InputStream in)
throws DriverEntityManagerException
{
File file = getDriverLibraryFile(driverEntity.getId(), libraryName);
BufferedOutputStream out = null;
try
{
out = new BufferedOutputStream(new FileOutputStream(file));
write(in, out);
}
catch (FileNotFoundException e)
{
throw new DriverEntityManagerException(e);
}
finally
{
close(out);
}
}
@Override
public synchronized void deleteDriverLibrary(DriverEntity driverEntity, String... libraryName)
throws DriverEntityManagerException
{
File directory = getDriverLibraryDirectory(driverEntity.getId(), false);
if (!directory.exists())
return;
for (String ln : libraryName)
{
File file = new File(directory, ln);
deleteFile(file);
}
}
@Override
public void deleteDriverLibrary(DriverEntity driverEntity) throws DriverEntityManagerException
{
File directory = getDriverLibraryDirectory(driverEntity.getId(), false);
deleteFileIn(directory);
}
@Override
public InputStream getDriverLibrary(DriverEntity driverEntity, String libraryName)
throws DriverEntityManagerException
{
File file = getDriverLibraryFile(driverEntity.getId(), libraryName);
try
{
return new BufferedInputStream(new FileInputStream(file));
}
catch (FileNotFoundException e)
{
throw new DriverLibraryNotFoundException(driverEntity, libraryName);
}
}
@Override
public void readDriverLibrary(DriverEntity driverEntity, String libraryName, OutputStream out)
throws DriverEntityManagerException
{
File file = getDriverLibraryFile(driverEntity.getId(), libraryName);
InputStream in = null;
try
{
in = new BufferedInputStream(new FileInputStream(file));
write(in, out);
}
catch (FileNotFoundException e)
{
throw new DriverLibraryNotFoundException(driverEntity, libraryName);
}
finally
{
close(in);
}
}
@Override
public List<DriverLibraryInfo> getDriverLibraryInfos(DriverEntity driverEntity) throws DriverEntityManagerException
{
List<DriverLibraryInfo> driverLibraryInfos = new ArrayList<DriverLibraryInfo>();
File directory = getDriverLibraryDirectory(driverEntity.getId(), false);
if (directory.exists())
{
File[] files = directory.listFiles();
for (File file : files)
{
if (file.isDirectory())
continue;
DriverLibraryInfo driverLibraryInfo = new DriverLibraryInfo(file.getName(), file.length());
driverLibraryInfos.add(driverLibraryInfo);
}
}
return driverLibraryInfos;
}
@Override
public Driver getDriver(DriverEntity driverEntity) throws DriverEntityManagerException
{
PathDriverFactory pathDriverFactory = getPathDriverFactoryNotNull(driverEntity);
return pathDriverFactory.getDriver(driverEntity.getDriverClassName());
}
@Override
public void releaseDriver(DriverEntity driverEntity) throws DriverEntityManagerException
{
removePathDriverFactory(driverEntity);
}
@Override
public synchronized void releaseAllDrivers()
{
for (Map.Entry<String, PathDriverFactoryInfo> entry : this.pathDriverFactoryInfoMap.entrySet())
{
try
{
entry.getValue().getPathDriverFactory().release();
}
catch (Throwable t)
{
if (LOGGER.isErrorEnabled())
LOGGER.error("releaseAllDrivers", t);
}
}
this.pathDriverFactoryInfoMap = new HashMap<String, PathDriverFactoryInfo>();
}
/**
* 获取指定{@linkplain DriverEntity}{@linkplain PathDriverFactory}
* <p>
* 此方法不会返回{@code null}
* </p>
*
* @param driverEntity
* @return
* @throws PathDriverFactoryException
*/
protected synchronized PathDriverFactory getPathDriverFactoryNotNull(DriverEntity driverEntity)
throws PathDriverFactoryException
{
String driverEntityId = driverEntity.getId();
PathDriverFactory pathDriverFactory = null;
PathDriverFactoryInfo pathDriverFactoryInfo = this.pathDriverFactoryInfoMap.get(driverEntityId);
if (pathDriverFactoryInfo != null)
{
if (pathDriverFactoryInfo.isModifiedAfterCreation())
{
this.pathDriverFactoryInfoMap.remove(driverEntityId);
pathDriverFactoryInfo.getPathDriverFactory().release();
if (LOGGER.isDebugEnabled())
LOGGER.debug(" [" + pathDriverFactory + "] has been discarded for its path modification");
}
else
pathDriverFactory = pathDriverFactoryInfo.getPathDriverFactory();
}
if (pathDriverFactory == null)
{
pathDriverFactory = createPathDriverFactory(driverEntity);
pathDriverFactoryInfo = new PathDriverFactoryInfo(pathDriverFactory);
this.pathDriverFactoryInfoMap.put(driverEntityId, pathDriverFactoryInfo);
if (LOGGER.isDebugEnabled())
LOGGER.debug(" [" + pathDriverFactory + "] is created for loading drivers.");
}
return pathDriverFactory;
}
/**
* 移除{@linkplain PathDriverFactory}
*
* @param driverEntity
* @throws PathDriverFactoryException
*/
protected synchronized void removePathDriverFactory(DriverEntity driverEntity) throws PathDriverFactoryException
{
PathDriverFactoryInfo pathDriverFactoryInfo = this.pathDriverFactoryInfoMap.remove(driverEntity.getId());
if (pathDriverFactoryInfo == null)
return;
pathDriverFactoryInfo.getPathDriverFactory().release();
}
/**
* 创建{@linkplain PathDriverFactory}实例并对其进行初始化
*
* @param driverEntity
* @return
* @throws PathDriverFactoryException
*/
protected PathDriverFactory createPathDriverFactory(DriverEntity driverEntity) throws PathDriverFactoryException
{
File path = getDriverLibraryDirectory(driverEntity.getId(), true);
PathDriverFactory pathDriverFactory = new PathDriverFactory(path);
pathDriverFactory.init();
return pathDriverFactory;
}
/**
* 校验输入
*
* @param driverEntity
*/
protected void checkInput(DriverEntity driverEntity)
{
if (isBlank(driverEntity.getId()) || isBlank(driverEntity.getDriverClassName()))
throw new IllegalArgumentException();
}
protected boolean reloadDriverEntityFileIfModified()
{
long thisModified = this.driverEntityInfoFile.lastModified();
if (thisModified == this.driverEntityInfoFileLastModified)
return false;
read();
return true;
}
/**
* 从列表中移除相同的{@linkplain DriverEntity}
*
* @param driverEntities
* @param driverEntityId
* @return
*/
protected int removeExists(List<DriverEntity> driverEntities, String driverEntityId)
{
int removeCount = 0;
int index = -1;
while ((index = findDriverEntityIndex(driverEntities, driverEntityId)) >= 0)
{
driverEntities.remove(index);
removeCount++;
}
return removeCount;
}
/**
* 查找指定路径的{@linkplain DriverEntity}索引位置
*
* @param driverEntities
* @param driverEntityId
* @return
*/
protected int findDriverEntityIndex(List<DriverEntity> driverEntities, String driverEntityId)
{
if (driverEntities != null)
{
for (int i = driverEntities.size() - 1; i >= 0; i--)
{
DriverEntity driverEntity = driverEntities.get(i);
if (driverEntity.getId().equals(driverEntityId))
return i;
}
}
return -1;
}
/**
* 读取{@linkplain #driverEntities}
*
* @return
* @throws DriverEntityManagerException
*/
protected void read() throws DriverEntityManagerException
{
List<DriverEntity> driverEntities = null;
if (this.driverEntityInfoFile.exists())
driverEntities = readDriverEntities(this.driverEntityInfoFile);
else
driverEntities = new ArrayList<DriverEntity>();
this.driverEntities = driverEntities;
this.driverEntityInfoFileLastModified = this.driverEntityInfoFile.lastModified();
}
/**
* 从文件中读取{@linkplain DriverEntity}列表
*
* @param driverEntityInfoFile
* @return
* @throws DriverEntityManagerException
*/
protected abstract List<DriverEntity> readDriverEntities(File driverEntityInfoFile)
throws DriverEntityManagerException;
/**
* {@linkplain #driverEntities}列表写入文件
*
* @throws DriverEntityManagerException
*/
protected void write() throws DriverEntityManagerException
{
writeDriverEntities(this.driverEntities, this.driverEntityInfoFile);
}
/**
* {@linkplain DriverEntity}列表写入文件
*
* @param driverEntities
* @param driverEntityInfoFile
* @throws DriverEntityManagerException
*/
protected abstract void writeDriverEntities(List<DriverEntity> driverEntities, File driverEntityInfoFile)
throws DriverEntityManagerException;
/**
* 获取{@linkplain #driverEntityInfoFile}输入流
*
* @return
* @throws DriverEntityManagerException
*/
protected Reader getDriverEntityInfoFileReader() throws DriverEntityManagerException
{
try
{
return new BufferedReader(new InputStreamReader(new FileInputStream(this.driverEntityInfoFile),
this.driverEntityFileEncoding));
}
catch (FileNotFoundException e)
{
throw new DriverEntityManagerException(e);
}
catch (UnsupportedEncodingException e)
{
throw new DriverEntityManagerException(e);
}
}
/**
* 获取{@linkplain #driverEntityInfoFile}输出流
*
* @return
* @throws DriverEntityManagerException
*/
protected Writer getDriverEntityInfoFileWriter() throws DriverEntityManagerException
{
try
{
return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(this.driverEntityInfoFile),
this.driverEntityFileEncoding));
}
catch (FileNotFoundException e)
{
throw new DriverEntityManagerException(e);
}
catch (UnsupportedEncodingException e)
{
throw new DriverEntityManagerException(e);
}
}
/**
* 字符串是否为空
*
* @param s
* @return
*/
protected boolean isBlank(String s)
{
if (s == null)
return true;
if (s.isEmpty())
return true;
if (s.trim().isEmpty())
return true;
return false;
}
protected void close(Closeable closeable)
{
if (closeable == null)
return;
try
{
closeable.close();
}
catch (Exception e)
{
}
}
/**
* 获取驱动库文件
*
* @param driverEntityId
* @param driverLibraryFileName
* @return
*/
protected File getDriverLibraryFile(String driverEntityId, String driverLibraryFileName)
{
File path = getDriverLibraryDirectory(driverEntityId, true);
File file = new File(path, driverLibraryFileName);
return file;
}
/**
* 获取驱动库目录
*
* @param driverEntityId
* @param create
* @return
*/
protected File getDriverLibraryDirectory(String driverEntityId, boolean create)
{
File file = new File(this.rootDirectory, driverEntityId);
if (create && !file.exists())
file.mkdirs();
return file;
}
/**
* 删除目录下指定名称的文件
*
* @param driverEntityId
*/
protected void deleteDriverLibraryDirectory(String driverEntityId)
{
File file = getDriverLibraryDirectory(driverEntityId, false);
deleteFile(file);
}
/**
* 删除文件
*
* @param file
*/
protected void deleteFile(File file)
{
if (!file.exists())
return;
if (file.isDirectory())
{
File[] children = file.listFiles();
for (File child : children)
deleteFile(child);
}
file.delete();
}
/**
* 删除文件夹里的所有文件
*
* @param directory
*/
protected void deleteFileIn(File directory)
{
if (!directory.exists())
return;
if (directory.isDirectory())
{
File[] children = directory.listFiles();
for (File child : children)
deleteFile(child);
}
}
/**
* 读取输入流并写入输出流
*
* @param in
* @param out
* @throws IOException
*/
protected void write(InputStream in, OutputStream out) throws DriverEntityManagerException
{
byte[] cache = new byte[1024];
int readLen = -1;
try
{
while ((readLen = in.read(cache)) > -1)
out.write(cache, 0, readLen);
}
catch (IOException e)
{
throw new DriverEntityManagerException(e);
}
}
protected static class PathDriverFactoryInfo
{
private final PathDriverFactory pathDriverFactory;
private final long lastModifiedOnCreation;
public PathDriverFactoryInfo(PathDriverFactory pathDriverFactory)
{
super();
this.pathDriverFactory = pathDriverFactory;
this.lastModifiedOnCreation = this.pathDriverFactory.getPathLastModified();
}
public PathDriverFactory getPathDriverFactory()
{
return pathDriverFactory;
}
public long getLastModifiedOnCreation()
{
return lastModifiedOnCreation;
}
public boolean isModifiedAfterCreation()
{
return this.pathDriverFactory.getPathLastModified() > this.lastModifiedOnCreation;
}
}
}

View File

@ -0,0 +1,224 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Properties;
/**
* JDBC连接选项
*
* @author datagear@163.com
*
*/
public class ConnectionOption implements Serializable
{
private static final long serialVersionUID = 1L;
public static final String PROPERTY_NAME_USER = "user";
public static final String PROPERTY_NAME_PASSWORD = "password";
private String url;
private Properties properties = new Properties();
public ConnectionOption()
{
super();
}
public ConnectionOption(String url)
{
super();
this.url = url;
}
public ConnectionOption(String url, String user)
{
super();
this.url = url;
this.properties.put(PROPERTY_NAME_USER, user);
}
public ConnectionOption(String url, String user, String password)
{
super();
this.url = url;
this.properties.put(PROPERTY_NAME_USER, user);
this.properties.put(PROPERTY_NAME_PASSWORD, password);
}
public String getUrl()
{
return url;
}
public void setUrl(String url)
{
this.url = url;
}
public Properties getProperties()
{
return properties;
}
public void setProperties(Properties properties)
{
this.properties = properties;
}
public String getUser()
{
return this.properties.getProperty(PROPERTY_NAME_USER);
}
public void setUser(String user)
{
this.properties.put(PROPERTY_NAME_USER, user);
}
public String getPassword()
{
return this.properties.getProperty(PROPERTY_NAME_PASSWORD);
}
public void setPassword(String password)
{
this.properties.put(PROPERTY_NAME_PASSWORD, password);
}
@SuppressWarnings("unchecked")
public <T> T getProperty(String name)
{
return (T) this.properties.getProperty(name);
}
public void setProperty(String name, Object value)
{
this.properties.put(name, value);
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((properties == null) ? 0 : properties.hashCode());
result = prime * result + ((url == null) ? 0 : url.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ConnectionOption other = (ConnectionOption) obj;
if (properties == null)
{
if (other.properties != null)
return false;
}
else if (!properties.equals(other.properties))
return false;
if (url == null)
{
if (other.url != null)
return false;
}
else if (!url.equals(other.url))
return false;
return true;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [url=" + url + ", properties=" + properties + "]";
}
/**
* 构建{@linkplain ConnectionOption}实例
*
* @return
*/
public static ConnectionOption valueOf()
{
return new ConnectionOption();
}
/**
* 构建{@linkplain ConnectionOption}实例
*
* @param url
* @return
*/
public static ConnectionOption valueOf(String url)
{
return new ConnectionOption(url);
}
/**
* 构建{@linkplain ConnectionOption}实例
*
* @param url
* @param user
* @return
*/
public static ConnectionOption valueOf(String url, String user)
{
return new ConnectionOption(url, user);
}
/**
* 构建{@linkplain ConnectionOption}实例
*
* @param url
* @param user
* @param password
* @return
*/
public static ConnectionOption valueOf(String url, String user, String password)
{
return new ConnectionOption(url, user, password);
}
/**
* 构建{@linkplain ConnectionOption}实例
* <p>
* 如果在从{@linkplain Connection}中获取连接信息时出现{@linkplain SQLException}
* 此方法将返回一个所有属性都为{@code null}的实例
* </p>
*
* @param cn
* @return
*/
public static ConnectionOption valueOf(Connection cn)
{
ConnectionOption connectionOption = new ConnectionOption();
try
{
DatabaseMetaData dbm = cn.getMetaData();
connectionOption.setUrl(dbm.getURL());
connectionOption.setUser(dbm.getUserName());
}
catch (SQLException e)
{
}
return connectionOption;
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.sql.Connection;
/**
* {@linkplain Connection}敏感器
*
* @author datagear@163.com
*
*/
public interface ConnectionSensor
{
/**
* 是否支持指定{@link Connection}
*
* @param cn
* @return
*/
boolean supports(Connection cn);
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.sql.Connection;
/**
* JDBC连接源
* <p>
* 它用于获取特定{@linkplain DriverEntity}的连接
* </p>
*
* @author datagear@163.com
*
*/
public interface ConnectionSource
{
/**
* 获取指定{@linkplain DriverEntity}{@linkplain Connection}
*
* @param driverEntity
* @param connectionOption
* @return
* @throws ConnectionSourceException
*/
Connection getConnection(DriverEntity driverEntity, ConnectionOption connectionOption)
throws ConnectionSourceException;
/**
* 获取{@linkplain Connection}
* <p>
* 此方法尝试获取最适合给定参数的{@linkplain Connection}
* </p>
*
* @param connectionOption
* @return
* @throws UnsupportedGetConnectionException
* 获取不到任何合适的{@linkplain Connection}
* @throws ConnectionSourceException
*/
Connection getConnection(ConnectionOption connectionOption)
throws UnsupportedGetConnectionException, ConnectionSourceException;
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
/**
* {@linkplain ConnectionSource}异常
*
* @author datagear@163.com
*
*/
public class ConnectionSourceException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public ConnectionSourceException()
{
super();
}
public ConnectionSourceException(String message)
{
super(message);
}
public ConnectionSourceException(Throwable cause)
{
super(cause);
}
public ConnectionSourceException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.sql.Connection;
/**
* 常量{@linkplain ConnectionSensor}
* <p>
* 此类的{@linkplain #supports(Connection)}方法始终返回{@linkplain #isSupported()}的值默认为{@code true}
* </p>
*
* @author datagear@163.com
*
*/
public class ConstantConnectionSensor implements ConnectionSensor
{
private boolean supported = true;
public ConstantConnectionSensor()
{
super();
}
public ConstantConnectionSensor(boolean supported)
{
super();
this.supported = supported;
}
public boolean isSupported()
{
return supported;
}
public void setSupported(boolean supported)
{
this.supported = supported;
}
@Override
public boolean supports(Connection cn)
{
return this.supported;
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
/**
* 常量{@linkplain URLSensor}
* <p>
* 此类的{@linkplain #supports(String)}方法始终返回{@linkplain #isSupported()}的值默认为{@code true}
* </p>
*
* @author datagear@163.com
*
*/
public class ConstantURLSensor implements URLSensor
{
private boolean supported = true;
public ConstantURLSensor()
{
super();
}
public ConstantURLSensor(boolean supported)
{
super();
this.supported = supported;
}
public boolean isSupported()
{
return supported;
}
public void setSupported(boolean supported)
{
this.supported = supported;
}
@Override
public boolean supports(String url)
{
return this.supported;
}
}

View File

@ -0,0 +1,363 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 默认{@linkplain ConnectionSource}实现
*
* @author datagear@163.com
*
*/
public class DefaultConnectionSource implements ConnectionSource
{
private static Logger LOGGER = LoggerFactory.getLogger(DefaultConnectionSource.class);
private DriverEntityManager driverEntityManager;
private DriverChecker driverChecker = new SimpleDriverChecker();
private ConcurrentMap<String, DriverEntity> urlPreferedDriverEntityMap = new ConcurrentHashMap<String, DriverEntity>();
private volatile long driverEntityManagerLastModified = -1;
public DefaultConnectionSource()
{
super();
}
public DefaultConnectionSource(DriverEntityManager driverEntityManager)
{
super();
this.driverEntityManager = driverEntityManager;
}
public DriverEntityManager getDriverEntityManager()
{
return driverEntityManager;
}
public void setDriverEntityManager(DriverEntityManager driverEntityManager)
{
this.driverEntityManager = driverEntityManager;
}
public DriverChecker getDriverChecker()
{
return driverChecker;
}
public void setDriverChecker(DriverChecker driverChecker)
{
this.driverChecker = driverChecker;
}
@Override
public Connection getConnection(DriverEntity driverEntity, ConnectionOption connectionOption)
throws ConnectionSourceException
{
Driver driver = this.driverEntityManager.getDriver(driverEntity);
if (!acceptsURL(driver, connectionOption.getUrl()))
throw new URLNotAcceptedException(driverEntity, connectionOption.getUrl());
return getConnection(driver, connectionOption);
}
@Override
public Connection getConnection(ConnectionOption connectionOption) throws ConnectionSourceException
{
Driver driver = findPreferredDriver(connectionOption);
if (LOGGER.isDebugEnabled())
LOGGER.debug("Found prefered Driver [" + toDriverString(driver) + "] for [" + connectionOption + "]");
return getConnection(driver, connectionOption);
}
/**
* 查找首选{@linkplain Driver}
*
* @param connectionOption
* @return
* @throws UnsupportedGetConnectionException
* 当找不到时抛出此异常
* @throws ConnectionSourceException
*/
protected Driver findPreferredDriver(ConnectionOption connectionOption)
throws UnsupportedGetConnectionException, ConnectionSourceException
{
Driver preferedDriver = null;
if (this.driverEntityManager.getLastModified() > this.driverEntityManagerLastModified)
{
this.driverEntityManagerLastModified = this.driverEntityManager.getLastModified();
this.urlPreferedDriverEntityMap.clear();
}
String url = connectionOption.getUrl();
DriverEntity preferedDriverEntity = this.urlPreferedDriverEntityMap.get(url);
if (preferedDriverEntity != null)
{
try
{
preferedDriver = this.driverEntityManager.getDriver(preferedDriverEntity);
}
catch (Throwable t)
{
if (LOGGER.isErrorEnabled())
LOGGER.error("Getting Driver by cached [" + preferedDriverEntity + "] for [" + connectionOption
+ "] error", t);
}
}
if (preferedDriver == null)
{
List<DriverEntityDriver> accepted = new ArrayList<DriverEntityDriver>();
List<DriverEntityDriver> checked = new ArrayList<DriverEntityDriver>();
List<DriverEntity> driverEntities = this.driverEntityManager.getAll();
for (DriverEntity driverEntity : driverEntities)
{
Driver driver = null;
try
{
driver = this.driverEntityManager.getDriver(driverEntity);
}
catch (Throwable t)
{
if (LOGGER.isErrorEnabled())
LOGGER.error("Getting Driver with [" + driverEntity + "] for finding prefered Driver for ["
+ connectionOption + "] error", t);
}
if (driver != null)
{
boolean accept = false;
try
{
accept = acceptsURL(driver, connectionOption.getUrl());
}
catch (Throwable t)
{
}
if (accept)
{
DriverEntityDriver driverEntityDriver = new DriverEntityDriver(driverEntity, driver);
accepted.add(driverEntityDriver);
try
{
if (this.driverChecker.check(driver, connectionOption, true))
checked.add(driverEntityDriver);
}
catch (Throwable t)
{
}
}
}
}
DriverEntityDriver preferedChecked = findPreferedDriverEntityDriver(checked);
if (preferedChecked != null)
{
preferedDriver = preferedChecked.getDriver();
this.urlPreferedDriverEntityMap.put(url, preferedChecked.getDriverEntity());
}
else
{
DriverEntityDriver preferedAccepted = findPreferedDriverEntityDriver(accepted);
// 如果连接参数有误比如密码错误会导致所有校验都不通过而抛出UnsupportedGetConnectionException异常不太合适
// 所以如果没有找到校验通过的那么就使用可接受URL的但是不应该加入缓存
if (preferedAccepted != null)
preferedDriver = preferedAccepted.getDriver();
}
}
if (preferedDriver == null)
throw new UnsupportedGetConnectionException(connectionOption);
return preferedDriver;
}
/**
* 查找首选{@linkplain DriverEntityDriver}
* <p>
* 如果差找不到将返回{@code null}
* </p>
*
* @param checked
* @return
*/
protected DriverEntityDriver findPreferedDriverEntityDriver(List<DriverEntityDriver> checked)
{
DriverEntityDriver prefered = null;
for (DriverEntityDriver candidate : checked)
{
if (prefered == null)
prefered = candidate;
else
{
if (candidate.isPreferedThan(prefered))
prefered = candidate;
}
}
return prefered;
}
protected boolean acceptsURL(Driver driver, String url)
{
try
{
return driver.acceptsURL(url);
}
catch (SQLException e)
{
throw new ConnectionSourceException(e);
}
}
/**
* 获取{@linkplain Connection}
*
* @param driver
* @param connectionOption
* @return
* @throws EstablishConnectionException
*/
protected Connection getConnection(Driver driver, ConnectionOption connectionOption)
throws EstablishConnectionException
{
Properties extraProperties = getExtraConnectionProperties(driver);
Properties properties = null;
if (extraProperties == null)
properties = connectionOption.getProperties();
else
{
properties = new Properties(extraProperties);
properties.putAll(connectionOption.getProperties());
}
try
{
return driver.connect(connectionOption.getUrl(), properties);
}
catch (SQLException e)
{
throw new EstablishConnectionException(connectionOption, e);
}
}
protected String toDriverString(Driver driver)
{
return driver.getClass().getName() + "[majorVersion=" + driver.getMajorVersion() + ", minorVersion="
+ driver.getMinorVersion() + "]";
}
/**
* 获取附加连接参数
* <p>
* 对于{@linkplain Connection}{@linkplain DatabaseMetaData}
* 中的部分接口不同的JDBC驱动可能需要配置各自的连接参数子类可重写此方法来实现
* </p>
* <p>
* 此方法默认返回{@code null}表示没有附加连接参数
* </p>
*
* @return
*/
protected Properties getExtraConnectionProperties(Driver driver)
{
return null;
}
protected static class DriverEntityDriver
{
private DriverEntity driverEntity;
private Driver driver;
public DriverEntityDriver()
{
super();
}
public DriverEntityDriver(DriverEntity driverEntity, Driver driver)
{
super();
this.driverEntity = driverEntity;
this.driver = driver;
}
public DriverEntity getDriverEntity()
{
return driverEntity;
}
public void setDriverEntity(DriverEntity driverEntity)
{
this.driverEntity = driverEntity;
}
public Driver getDriver()
{
return driver;
}
public void setDriver(Driver driver)
{
this.driver = driver;
}
/**
* 是否比给定{@linkplain DriverEntityDriver}更首选
*
* @param driverEntityDriver
* @return
*/
public boolean isPreferedThan(DriverEntityDriver driverEntityDriver)
{
int myMajorVersion = this.driver.getMajorVersion();
int myMinorVersion = this.driver.getMinorVersion();
int youMajorVersion = driverEntityDriver.driver.getMajorVersion();
int youMinorVersion = driverEntityDriver.driver.getMinorVersion();
if (myMajorVersion > youMajorVersion)
return true;
else if (myMajorVersion < youMajorVersion)
return false;
else
{
if (myMinorVersion > youMinorVersion)
return true;
else
return false;
}
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.sql.Driver;
/**
* {@linkplain Driver}校验器
*
* @author datagear@163.com
*
*/
public interface DriverChecker
{
/**
* 校验{@linkplain Driver}是否支持使用给定{@linkplain ConnectionOption}建立连接
* <p>
* 返回{@code false}或者抛出任何异常表明不支持
* </p>
*
* @param driver
* @param connectionOption
* @param ignoreAcceptsURLCheck
* 是否忽略{@linkplain Driver#acceptsURL(String)}校验
* @return
* @throws Throwable
*/
boolean check(Driver driver, ConnectionOption connectionOption, boolean ignoreAcceptsURLCheck) throws Throwable;
}

View File

@ -0,0 +1,148 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.io.Serializable;
/**
* JDBC驱动程序实体
*
* @author datagear@163.com
*
*/
public class DriverEntity implements Serializable
{
private static final long serialVersionUID = 1L;
/** ID */
private String id;
/** 驱动类名 */
private String driverClassName;
/** 展示名称 */
private String displayName;
/** 展示描述 */
private String displayDesc;
public DriverEntity()
{
super();
}
public DriverEntity(String id, String driverClassName)
{
super();
this.id = id;
this.driverClassName = driverClassName;
}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getDriverClassName()
{
return driverClassName;
}
public void setDriverClassName(String driverClassName)
{
this.driverClassName = driverClassName;
}
public boolean hasDisplayName()
{
return (this.displayName != null && !this.displayName.isEmpty());
}
public String getDisplayName()
{
return displayName;
}
public void setDisplayName(String displayName)
{
this.displayName = displayName;
}
public boolean hasDisplayDesc()
{
return (this.displayDesc != null && !this.displayDesc.isEmpty());
}
public String getDisplayDesc()
{
return displayDesc;
}
public void setDisplayDesc(String displayDesc)
{
this.displayDesc = displayDesc;
}
public String getDisplayText()
{
if (hasDisplayName())
return getDisplayName();
else
return getDriverClassName();
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DriverEntity other = (DriverEntity) obj;
if (id == null)
{
if (other.id != null)
return false;
}
else if (!id.equals(other.id))
return false;
return true;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [id=" + id + ", driverClassName=" + driverClassName + ", displayName="
+ displayName + ", displayDesc=" + displayDesc + "]";
}
/**
* 构建{@linkplain DriverEntity}
*
* @param id
* @param driverClassName
* @return
*/
public static DriverEntity valueOf(String id, String driverClassName)
{
return new DriverEntity(id, driverClassName);
}
}

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Driver;
import java.util.List;
/**
* {@linkplain DriverEntity}管理器
*
* @author datagear@163.com
*
*/
public interface DriverEntityManager
{
/**
* 添加{@linkplain DriverEntity}
*
* @param driverEntities
* @throws DriverEntityManagerException
*/
void add(DriverEntity... driverEntities) throws DriverEntityManagerException;
/**
* 更新{@linkplain DriverEntity}
*
* @param driverEntities
* @return
* @throws DriverEntityManagerException
*/
boolean[] update(DriverEntity... driverEntities) throws DriverEntityManagerException;
/**
* 获取指定路径的{@linkplain DriverEntity}
*
* @param id
* @return
* @throws DriverEntityManagerException
*/
DriverEntity get(String id) throws DriverEntityManagerException;
/**
* 删除指定ID的{@linkplain DriverEntity}
*
* @param ids
* @return
* @throws DriverEntityManagerException
*/
void delete(String... ids) throws DriverEntityManagerException;
/**
* 获取所有{@linkplain DriverEntity}
*
* @return
* @throws DriverEntityManagerException
*/
List<DriverEntity> getAll() throws DriverEntityManagerException;
/**
* 获取上次变更时间
*
* @return
* @throws DriverEntityManagerException
*/
long getLastModified() throws DriverEntityManagerException;
/**
* 添加驱动程序库
*
* @param driverEntity
* @param libraryName
* @param in
* @throws DriverEntityManagerException
*/
void addDriverLibrary(DriverEntity driverEntity, String libraryName, InputStream in)
throws DriverEntityManagerException;
/**
* 删除驱动程序库
*
* @param driverEntity
* @param libraryName
* @throws DriverEntityManagerException
*/
void deleteDriverLibrary(DriverEntity driverEntity, String... libraryName) throws DriverEntityManagerException;
/**
* 删除驱动程序所有库
*
* @param driverEntity
* @throws DriverEntityManagerException
*/
void deleteDriverLibrary(DriverEntity driverEntity) throws DriverEntityManagerException;
/**
* 获取驱动程序库输出流
*
* @param driverEntity
* @param libraryName
* @return
* @throws DriverEntityManagerException
*/
InputStream getDriverLibrary(DriverEntity driverEntity, String libraryName) throws DriverEntityManagerException;
/**
* 读取驱动程序库
*
* @param driverEntity
* @param libraryName
* @param out
* @throws DriverEntityManagerException
*/
void readDriverLibrary(DriverEntity driverEntity, String libraryName, OutputStream out)
throws DriverEntityManagerException;
/**
* 获取{@linkplain DriverLibraryInfo}列表
*
* @param driverEntity
* @return
* @throws DriverEntityManagerException
*/
List<DriverLibraryInfo> getDriverLibraryInfos(DriverEntity driverEntity) throws DriverEntityManagerException;
/**
* 获取驱动程序
*
* @param driverEntity
* @return
* @throws DriverEntityManagerException
*/
Driver getDriver(DriverEntity driverEntity) throws DriverEntityManagerException;
/**
* 释放驱动程序库资源
*
* @param driverEntity
* @throws DriverEntityManagerException
*/
void releaseDriver(DriverEntity driverEntity) throws DriverEntityManagerException;
/**
* 释放所有驱动程序库资源
*/
void releaseAllDrivers();
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
/**
* {@linkplain DriverEntityManager}异常
*
* @author datagear@163.com
*
*/
public class DriverEntityManagerException extends ConnectionSourceException
{
private static final long serialVersionUID = 1L;
public DriverEntityManagerException()
{
super();
}
public DriverEntityManagerException(String message)
{
super(message);
}
public DriverEntityManagerException(Throwable cause)
{
super(cause);
}
public DriverEntityManagerException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.io.Serializable;
/**
* 驱动库信息
*
* @author datagear@163.com
*
*/
public class DriverLibraryInfo implements Serializable
{
private static final long serialVersionUID = 1L;
/** 库名称 */
private String name;
/** 库字节数大小 */
private long size;
public DriverLibraryInfo()
{
super();
}
public DriverLibraryInfo(String name, long size)
{
super();
this.name = name;
this.size = size;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public long getSize()
{
return size;
}
public void setSize(long size)
{
this.size = size;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", size=" + size + "]";
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
/**
* {@linkplain DriverEntityManager}在未找到指定名称的驱动库时将抛出此异常
*
* @author datagear@163.com
*
*/
public class DriverLibraryNotFoundException extends DriverEntityManagerException
{
private static final long serialVersionUID = 1L;
private DriverEntity driverEntity;
private String libraryName;
public DriverLibraryNotFoundException(DriverEntity driverEntity, String libraryName)
{
super(driverEntity + " 's library [" + libraryName + "] not found");
this.driverEntity = driverEntity;
this.libraryName = libraryName;
}
public DriverEntity getDriverEntity()
{
return driverEntity;
}
public String getLibraryName()
{
return libraryName;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
/**
* 未找到驱动异常
*
* @author datagear@163.com
*
*/
public class DriverNotFoundException extends PathDriverFactoryException
{
private static final long serialVersionUID = 1L;
private String driverPath;
private String driverClassName;
public DriverNotFoundException(String driverPath, String driverClassName)
{
this(driverPath, driverClassName, null);
}
public DriverNotFoundException(String driverPath, String driverClassName, Throwable cause)
{
super("Driver [" + driverClassName + "] not found int path [" + driverPath + "]", cause);
this.driverPath = driverPath;
this.driverClassName = driverClassName;
}
public String getDriverPath()
{
return driverPath;
}
public String getDriverClassName()
{
return driverClassName;
}
}

View File

@ -0,0 +1,160 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* JDBC驱动程序工具类
*
* @author datagear@163.com
*
*/
public class DriverTool
{
public DriverTool()
{
super();
}
/**
* 获取指定名称的已注册{@linkplain Driver}
* <p>
* 如果没有此方法将返回{@code null}
* </p>
*
* @param driverClassName
* @return
*/
public Driver getDriver(String driverClassName)
{
Set<Driver> drivers = getDrivers();
for (Driver driver : drivers)
{
if (isAssignableClass(driver.getClass(), driverClassName))
return driver;
}
return null;
}
/**
* 获取已注册的{@linkplain Driver}
* <p>
* 如果没有此方法将返回空{@linkplain Set}
* </p>
*
* @return
*/
public Set<Driver> getDrivers()
{
Set<Driver> myDrivers = new HashSet<Driver>();
ClassLoader myClassLoader = getClass().getClassLoader();
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements())
{
Driver driver = drivers.nextElement();
ClassLoader classLoader = driver.getClass().getClassLoader();
if (myClassLoader == classLoader)
myDrivers.add(driver);
}
return myDrivers;
}
/**
* 撤销已注册的{@linkplain Driver}
*
* @return
* @throws SQLException
*/
public List<String> deregisterDrivers() throws SQLException
{
// 此方法内容完全拷贝自org.apache.catalina.loader.JdbcLeakPrevention.clearJdbcDriverRegistrations()方法的内容
List<String> driverNames = new ArrayList<String>();
/*
* DriverManager.getDrivers() has a nasty side-effect of registering
* drivers that are visible to this class loader but haven't yet been
* loaded. Therefore, the first call to this method a) gets the list of
* originally loaded drivers and b) triggers the unwanted side-effect.
* The second call gets the complete list of drivers ensuring that both
* original drivers and any loaded as a result of the side-effects are
* all de-registered.
*/
HashSet<Driver> originalDrivers = new HashSet<Driver>();
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements())
{
originalDrivers.add(drivers.nextElement());
}
drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements())
{
Driver driver = drivers.nextElement();
// Only unload the drivers this web app loaded
if (driver.getClass().getClassLoader() != this.getClass().getClassLoader())
{
continue;
}
// Only report drivers that were originally registered. Skip any
// that were registered as a side-effect of this code.
if (originalDrivers.contains(driver))
{
driverNames.add(driver.getClass().getCanonicalName());
}
DriverManager.deregisterDriver(driver);
}
return driverNames;
}
/**
* 判断类是否是指定类名的类或者子类
*
* @param subClass
* @param superClassName
* @return
*/
protected boolean isAssignableClass(Class<?> subClass, String superClassName)
{
if (subClass == null)
return false;
if (subClass.getName().equals(superClassName))
return true;
Class<?> superClass = subClass.getSuperclass();
if (superClass != null && isAssignableClass(superClass, superClassName))
return true;
Class<?>[] superInterfaces = subClass.getInterfaces();
if (superInterfaces != null)
{
for (Class<?> superInterface : superInterfaces)
{
if (isAssignableClass(superInterface, superClassName))
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
/**
* 建立数据库连接出现异常
*
* @author datagear@163.com
*
*/
public class EstablishConnectionException extends ConnectionSourceException
{
private static final long serialVersionUID = 1L;
private ConnectionOption connectionOption;
public EstablishConnectionException(ConnectionOption connectionOption, Throwable cause)
{
super(cause);
this.connectionOption = connectionOption;
}
public ConnectionOption getConnectionOption()
{
return connectionOption;
}
}

View File

@ -0,0 +1,234 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* JDBC工具支持类
*
* @author datagear@163.com
*
*/
public class JdbcUtil
{
public JdbcUtil()
{
}
/**
* 获取指定类型的列值
*
* @param rs
* @param row
* @param columnIndex
* @param targetType
* @return
* @throws SQLException
*/
public static Object getColumnValue(ResultSet rs, long row, int columnIndex, Class<?> targetType)
throws SQLException
{
if (String.class.equals(targetType))
{
return rs.getString(columnIndex);
}
else if (int.class.equals(targetType))
{
return rs.getInt(columnIndex);
}
else if (Integer.class.equals(targetType))
{
Integer v = rs.getInt(columnIndex);
return (rs.wasNull() ? null : v);
}
else if (boolean.class.equals(targetType))
{
return rs.getBoolean(columnIndex);
}
else if (Boolean.class.equals(targetType))
{
Boolean v = rs.getBoolean(columnIndex);
return (rs.wasNull() ? null : v);
}
else if (long.class.equals(targetType))
{
return rs.getLong(columnIndex);
}
else if (Long.class.equals(targetType))
{
long v = rs.getLong(columnIndex);
return (rs.wasNull() ? null : v);
}
else if (short.class.equals(targetType))
{
return rs.getShort(columnIndex);
}
else if (Short.class.equals(targetType))
{
short v = rs.getShort(columnIndex);
return (rs.wasNull() ? null : v);
}
else if (float.class.equals(targetType))
{
return rs.getFloat(columnIndex);
}
else if (Float.class.equals(targetType))
{
float v = rs.getFloat(columnIndex);
return (rs.wasNull() ? null : v);
}
else if (double.class.equals(targetType))
{
return rs.getDouble(columnIndex);
}
else if (Double.class.equals(targetType))
{
double v = rs.getDouble(columnIndex);
return (rs.wasNull() ? null : v);
}
else if (java.util.Date.class.equals(targetType))
{
return rs.getDate(columnIndex);
}
else if (BigDecimal.class.equals(targetType))
{
return rs.getBigDecimal(columnIndex);
}
else if (BigInteger.class.equals(targetType))
{
BigDecimal bd = rs.getBigDecimal(columnIndex);
return (bd == null ? null : bd.toBigInteger());
}
else if (byte.class.equals(targetType))
{
return rs.getByte(columnIndex);
}
else if (Byte.class.equals(targetType))
{
byte v = rs.getByte(columnIndex);
return (rs.wasNull() ? null : v);
}
else if (byte[].class.equals(targetType))
{
return rs.getBytes(columnIndex);
}
else if (char.class.equals(targetType))
{
String v = rs.getString(columnIndex);
return (v == null ? (char) 0 : v.charAt(0));
}
else if (Character.class.equals(targetType))
{
String v = rs.getString(columnIndex);
return (v == null ? null : v.charAt(0));
}
else if (java.sql.Date.class.equals(targetType))
{
return rs.getDate(columnIndex);
}
else if (java.sql.Timestamp.class.equals(targetType))
{
return rs.getTimestamp(columnIndex);
}
else if (java.sql.Time.class.equals(targetType))
{
return rs.getTime(columnIndex);
}
else if (Enum.class.isAssignableFrom(targetType))
{
String enumName = rs.getString(columnIndex);
if (rs.wasNull() || enumName == null || enumName.isEmpty())
return null;
else
{
@SuppressWarnings({ "unchecked", "rawtypes" })
Enum em = Enum.valueOf((Class<Enum>) targetType, enumName);
return em;
}
}
else if (Object.class.equals(targetType))
{
return rs.getObject(columnIndex);
}
else
throw new UnsupportedOperationException("Getting [" + ResultSet.class.getName() + "] column value of type ["
+ targetType.getName() + "] is not supported");
}
/**
* 关闭{@linkplain Connection}
*
* @param cn
*/
public static void closeConnection(Connection cn)
{
if (cn == null)
return;
try
{
cn.close();
}
catch (Exception t)
{
}
}
/**
* 关闭{@linkplain Statement}
*
* @param st
*/
public static void closeStatement(Statement st)
{
if (st == null)
return;
try
{
st.close();
}
catch (Exception t)
{
}
}
/**
* 关闭{@linkplain ResultSet}
*
* @param rs
*/
public static void closeResultSet(ResultSet rs)
{
if (rs == null)
return;
try
{
rs.close();
}
catch (Exception t)
{
}
}
}

View File

@ -0,0 +1,516 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 特定路径类加载器
* <p>
* 注意此类会优先从{@linkplain #getPath()}路径中加载类因此不应该将标准库放入此路径中
* </p>
*
* @author datagear@163.com
*
*/
public class PathClassLoader extends ClassLoader
{
private static final Logger LOGGER = LoggerFactory.getLogger(PathClassLoader.class);
private static final String CLASS_FILE_SUFFIX = ".class";
/** 类路径 */
private File path;
/** 要强制加载类路径之外的类名套集 */
private Set<String> outsideForceLoads;
public PathClassLoader(String path)
{
this(new File(path), null);
}
public PathClassLoader(File path)
{
this(path, null);
}
public PathClassLoader(String path, ClassLoader parent)
{
this(new File(path), parent);
}
public PathClassLoader(File path, ClassLoader parent)
{
super(parent);
this.path = path;
}
public File getPath()
{
return path;
}
public Set<String> getOutsideForceLoads()
{
return outsideForceLoads;
}
public void setOutsideForceLoads(Set<String> outsideForceLoads)
{
this.outsideForceLoads = outsideForceLoads;
}
public void setOutsideForceLoads(String... outsideForceLoads)
{
this.outsideForceLoads = new HashSet<String>();
for (String outsideForceLoad : outsideForceLoads)
this.outsideForceLoads.add(outsideForceLoad);
}
/**
* 关闭
*/
public void close()
{
}
@Override
protected void finalize() throws Throwable
{
close();
}
@Override
public InputStream getResourceAsStream(String name)
{
ResourceInfo resourceInfo = findResourceInfo(name);
if (resourceInfo == null)
{
ClassLoader parent = getParent();
if (parent == null)
parent = PathClassLoader.class.getClassLoader();
return parent.getResourceAsStream(name);
}
byte[] bytes = null;
try
{
bytes = getResourceBytes(resourceInfo);
}
catch (IOException e)
{
return null;
}
if (LOGGER.isDebugEnabled())
LOGGER.debug("Got resource as stream " + "[" + name + "] in file ["
+ resourceInfo.getFileOfResource().getPath() + "]");
return new ByteArrayInputStream(bytes);
}
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
Class<?> clazz = findLoadedClass(name);
if (clazz == null)
{
try
{
clazz = findClass(name);
}
catch (Exception e)
{
}
}
if (clazz == null)
{
// 强制在此加载的类
if (this.outsideForceLoads != null && this.outsideForceLoads.contains(name))
{
InputStream in = getResourceAsStream(classNameToPath(name));
if (in == null)
throw new ClassNotFoundException(name);
byte[] bytes = null;
try
{
bytes = getBytes(in);
}
catch (IOException e)
{
throw new ClassNotFoundException(name, e);
}
clazz = defineClass(name, bytes, 0, bytes.length);
}
}
if (clazz == null)
{
ClassLoader parent = getParent();
if (parent == null)
parent = PathClassLoader.class.getClassLoader();
// 先尝试父加载器加载类这样可以避免Java标准库被覆盖
if (clazz == null)
{
try
{
clazz = Class.forName(name, false, parent);
}
catch (ClassNotFoundException e)
{
}
}
}
if (resolve)
resolveClass(clazz);
return clazz;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
String classFilePath = classNameToPath(name);
ResourceInfo classResourceInfo = findResourceInfo(classFilePath);
if (classResourceInfo == null)
throw new ClassNotFoundException(name);
byte[] classBytes = null;
try
{
classBytes = getResourceBytes(classResourceInfo);
}
catch (IOException e)
{
throw new ClassNotFoundException(name, e);
}
Class<?> clazz = defineClass(name, classBytes, 0, classBytes.length);
if (LOGGER.isDebugEnabled())
LOGGER.debug("Found and loaded class [" + name + "] in file ["
+ classResourceInfo.getFileOfResource().getPath() + "]");
return clazz;
}
/**
* 获取{@linkplain ResourceInfo}的字节数组
*
* @param resourceInfo
* @return
* @throws IOException
*/
protected byte[] getResourceBytes(ResourceInfo resourceInfo) throws IOException
{
try
{
return getBytes(resourceInfo.getIn());
}
finally
{
close(resourceInfo.getIn());
if (resourceInfo.hasJarFileOfResource())
close(resourceInfo.getJarFileOfResource());
}
}
/**
* 查找资源的{@linkplain ResourceInfo}
*
* @param resourcePath
* @return
*/
protected ResourceInfo findResourceInfo(String resourcePath)
{
InputStream in = null;
File fileOfResource = null;
JarFile jarFileOfResource = null;
// 尝试从文件路径加载
if (this.path.isDirectory())
{
File file = new File(this.path, resourcePath);
if (file.exists())
{
try
{
in = new FileInputStream(file);
fileOfResource = file;
}
catch (FileNotFoundException e)
{
in = null;
}
}
}
if (in == null)
{
List<File> files = getFilesOfJar(this.path);
for (int i = 0, len = files.size(); i < len; i++)
{
File file = files.get(i);
boolean isMe = false;
JarFile tmpJarFile = null;
try
{
tmpJarFile = new JarFile(file);
JarEntry jarEntry = tmpJarFile.getJarEntry(resourcePath);
if (jarEntry != null)
{
in = tmpJarFile.getInputStream(jarEntry);
fileOfResource = file;
jarFileOfResource = tmpJarFile;
isMe = true;
break;
}
}
catch (IOException e)
{
}
finally
{
if (!isMe)
close(tmpJarFile);
}
}
}
if (in == null)
return null;
ResourceInfo resourceInfo = new ResourceInfo(in, fileOfResource, jarFileOfResource);
return resourceInfo;
}
/**
* 读取输入流字节数组
*
* @param in
* @return
* @throws IOException
*/
protected byte[] getBytes(InputStream in) throws IOException
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int readLen = 0;
while ((readLen = in.read(buffer)) > 0)
bos.write(buffer, 0, readLen);
byte[] bytes = bos.toByteArray();
return bytes;
}
/**
* 获取jar文件列表
*
* @param path
* @return
*/
protected List<File> getFilesOfJar(File path)
{
if (path.isDirectory())
{
File[] files = path.listFiles(new FileFilter()
{
@Override
public boolean accept(File pathname)
{
return isJarFile(pathname);
}
});
return Arrays.asList(files);
}
else
{
List<File> jarFiles = new ArrayList<File>();
if (isJarFile(path))
jarFiles.add(path);
return jarFiles;
}
}
/**
* 判断文件是否是jar文件
*
* @param file
* @return
*/
protected boolean isJarFile(File file)
{
if (file.isDirectory())
return false;
String fileName = file.getName().toLowerCase();
return (fileName.endsWith(".jar") || fileName.endsWith(".zip"));
}
/**
* 将类名转换为路径名
*
* @param className
* @return
*/
protected String classNameToPath(String className)
{
StringBuilder path = new StringBuilder();
path.append(className.replace('.', '/'));
path.append(CLASS_FILE_SUFFIX);
return path.toString();
}
/**
* 关闭{@linkplain Closeable}
*/
protected void close(Closeable closeable)
{
if (closeable == null)
return;
try
{
closeable.close();
}
catch (Throwable t)
{
}
}
/**
* 关闭{@linkplain ZipFile}
*
* @param zipFile
*/
protected void close(ZipFile zipFile)
{
if (zipFile == null)
return;
try
{
zipFile.close();
}
catch (Throwable t)
{
}
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [path=" + path + "]";
}
protected static class ResourceInfo
{
private InputStream in;
private File fileOfResource;
private JarFile jarFileOfResource;
public ResourceInfo()
{
super();
}
public ResourceInfo(InputStream in, File fileOfResource, JarFile jarFileOfResource)
{
super();
this.in = in;
this.fileOfResource = fileOfResource;
this.jarFileOfResource = jarFileOfResource;
}
public InputStream getIn()
{
return in;
}
public void setIn(InputStream in)
{
this.in = in;
}
public File getFileOfResource()
{
return fileOfResource;
}
public void setFileOfResource(File fileOfResource)
{
this.fileOfResource = fileOfResource;
}
public boolean hasJarFileOfResource()
{
return jarFileOfResource != null;
}
public JarFile getJarFileOfResource()
{
return jarFileOfResource;
}
public void setJarFileOfResource(JarFile jarFileOfResource)
{
this.jarFileOfResource = jarFileOfResource;
}
}
}

View File

@ -0,0 +1,239 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.sql.Driver;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 特定路径JDBC驱动工厂
* <p>
* 注意创建实例后要调用{@linkplain #init()}初始化
* </p>
*
* @author datagear@163.com
*
*/
public class PathDriverFactory
{
private static final Logger LOGGER = LoggerFactory.getLogger(PathDriverFactory.class);
/** 驱动程序库路径 */
private File path;
private PathClassLoader pathClassLoader;
private Object driverTool;
public PathDriverFactory(String path)
{
this(new File(path));
}
public PathDriverFactory(File path)
{
super();
this.path = path;
}
public File getPath()
{
return path;
}
/**
* 初始化
*
* @throws PathDriverFactoryException
*/
public synchronized void init() throws PathDriverFactoryException
{
if (this.pathClassLoader != null)
return;
this.pathClassLoader = new PathClassLoader(this.path);
this.pathClassLoader.setOutsideForceLoads(DriverTool.class.getName());
try
{
Class<?> driverToolClass = this.pathClassLoader.loadClass(DriverTool.class.getName());
this.driverTool = driverToolClass.newInstance();
}
catch (ClassNotFoundException e)
{
throw new PathDriverFactoryException(e);
}
catch (InstantiationException e)
{
throw new PathDriverFactoryException(e);
}
catch (IllegalAccessException e)
{
throw new PathDriverFactoryException(e);
}
}
/**
* 获取路径上次修改时间
*
* @return
*/
public long getPathLastModified()
{
return getPathLastModified(this.path);
}
/**
* 获取指定类名的JDBC驱动程序
*
* @param driverClassName
* @return
* @throws PathDriverFactoryException
*/
public synchronized Driver getDriver(String driverClassName) throws PathDriverFactoryException
{
try
{
Class.forName(driverClassName, true, this.pathClassLoader);
}
catch (ClassNotFoundException e)
{
throw new DriverNotFoundException(this.path.getPath(), driverClassName, e);
}
catch (Throwable t)
{
// 可能会出现Error这里也一并捕获包装
throw new PathDriverFactoryException(t);
}
try
{
Driver driver = (Driver) this.driverTool.getClass().getMethod("getDriver", String.class)
.invoke(this.driverTool, driverClassName);
if (driver == null)
throw new PathDriverFactoryException("No Driver named [" + driverClassName + "] can be found in ["
+ this.pathClassLoader.getPath() + "]");
if (LOGGER.isDebugEnabled())
LOGGER.debug("Got JDBC driver [" + driverClassName + "] in path [" + this.path + "]");
return driver;
}
catch (IllegalArgumentException e)
{
throw new PathDriverFactoryException(e);
}
catch (SecurityException e)
{
throw new PathDriverFactoryException(e);
}
catch (IllegalAccessException e)
{
throw new PathDriverFactoryException(e);
}
catch (InvocationTargetException e)
{
throw new PathDriverFactoryException(e);
}
catch (NoSuchMethodException e)
{
throw new PathDriverFactoryException(e);
}
}
/**
* 释放资源
*
* @throws PathDriverFactoryException
*/
public synchronized void release() throws PathDriverFactoryException
{
releaseJdbcDrivers();
}
/**
* 释放{@linkplain Driver}资源
*
* @throws PathDriverFactoryException
*/
protected void releaseJdbcDrivers() throws PathDriverFactoryException
{
try
{
@SuppressWarnings("unchecked")
List<String> driverNames = (List<String>) this.driverTool.getClass().getMethod("deregisterDrivers")
.invoke(this.driverTool);
if (LOGGER.isDebugEnabled())
LOGGER.debug("deregister JDBC drivers loaded in path [" + this.path + "] :" + driverNames);
}
catch (IllegalArgumentException e)
{
throw new PathDriverFactoryException(e);
}
catch (SecurityException e)
{
throw new PathDriverFactoryException(e);
}
catch (IllegalAccessException e)
{
throw new PathDriverFactoryException(e);
}
catch (InvocationTargetException e)
{
throw new PathDriverFactoryException(e);
}
catch (NoSuchMethodException e)
{
throw new PathDriverFactoryException(e);
}
}
protected PathClassLoader getPathClassLoader()
{
return pathClassLoader;
}
/**
* 获取路径的上次修改时间
*
* @param path
* @return
*/
protected long getPathLastModified(File path)
{
long pathLastModified = path.lastModified();
if (path.isDirectory())
{
File[] children = path.listFiles();
if (children != null)
{
for (File child : children)
{
long childLastModified = child.lastModified();
if (childLastModified > pathLastModified)
pathLastModified = childLastModified;
}
}
}
return pathLastModified;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [path=" + path + "]";
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
/**
* {@linkplain PathDriverFactory}异常
*
* @author datagear@163.com
*
*/
public class PathDriverFactoryException extends DriverEntityManagerException
{
private static final long serialVersionUID = 1L;
public PathDriverFactoryException()
{
super();
}
public PathDriverFactoryException(String message)
{
super(message);
}
public PathDriverFactoryException(Throwable cause)
{
super(cause);
}
public PathDriverFactoryException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
/**
* 以前缀判断的{@linkplain URLSensor}
*
* @author datagear@163.com
*
*/
public class PrefixURLSensor implements URLSensor
{
private String prefix;
public PrefixURLSensor()
{
super();
}
public PrefixURLSensor(String prefix)
{
super();
this.prefix = prefix;
}
public String getPrefix()
{
return prefix;
}
public void setPrefix(String prefix)
{
this.prefix = prefix;
}
@Override
public boolean supports(String url)
{
return url.startsWith(this.prefix);
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.sql.Driver;
/**
* 简单{@linkplain DriverChecker}实现
* <p>
* 它仅使用{@linkplain Driver#acceptsURL(String)}校验
* </p>
*
* @author datagear@163.com
*
*/
public class SimpleDriverChecker extends AbstractDriverChecker
{
public SimpleDriverChecker()
{
super();
}
@Override
protected boolean checkConnection(Driver driver, ConnectionOption connectionOption) throws Throwable
{
return true;
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 使用URL进行判断的{@linkplain ConnectionSensor}
*
* @author datagear@163.com
*
*/
public class URLConnectionSensor implements ConnectionSensor
{
private URLSensor urlSensor;
public URLConnectionSensor()
{
super();
}
public URLConnectionSensor(URLSensor urlSensor)
{
super();
this.urlSensor = urlSensor;
}
public URLSensor getUrlSensor()
{
return urlSensor;
}
public void setUrlSensor(URLSensor urlSensor)
{
this.urlSensor = urlSensor;
}
@Override
public boolean supports(Connection cn)
{
String url = getURL(cn);
return (url == null ? false : this.urlSensor.supports(url));
}
/**
* 获取指定{@linkplain Connection}的连接URL
* <p>
* 如果出现{@linkplain SQLException}此方法将返回{@code null}
* </p>
*
* @param cn
* @return
*/
protected String getURL(Connection cn)
{
String url = null;
try
{
url = cn.getMetaData().getURL();
}
catch (SQLException e)
{
}
return url;
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
/**
* {@linkplain DriverEntity}对应的驱动不支持给定的URL异常
*
* @author datagear@163.com
*
*/
public class URLNotAcceptedException extends ConnectionSourceException
{
private static final long serialVersionUID = 1L;
private DriverEntity driverEntity;
private String url;
public URLNotAcceptedException(DriverEntity driverEntity, String url)
{
super(driverEntity + " 's driver can not accept url [" + url + "]");
this.driverEntity = driverEntity;
this.url = url;
}
public DriverEntity getDriverEntity()
{
return driverEntity;
}
public String getUrl()
{
return url;
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
/**
* JDBC连接URL敏感器
*
* @author datagear@163.com
*
*/
public interface URLSensor
{
/**
* 是否支持指定JDBC连接url
*
* @param url
* @return
*/
boolean supports(String url);
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
/**
* 不支持获取连接操作异常
* <p>
* {@linkplain DefaultConnectionSource#getConnection(ConnectionOption)}在无法获取合适的连接时将抛出此异常
* </p>
*
* @author datagear@163.com
*
*/
public class UnsupportedGetConnectionException extends ConnectionSourceException
{
private static final long serialVersionUID = 1L;
private ConnectionOption connectionOption;
public UnsupportedGetConnectionException(ConnectionOption connectionOption)
{
super("Get connection for [" + connectionOption + "] is not supported");
this.connectionOption = connectionOption;
}
public ConnectionOption getConnectionOption()
{
return connectionOption;
}
}

View File

@ -0,0 +1,195 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import java.io.File;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* 基于XML文件的{@linkplain DriverEntityManager}
*
* @author datagear@163.com
*
*/
public class XmlDriverEntityManager extends AbstractFileDriverEntityManager
{
private static final Logger LOGGER = LoggerFactory.getLogger(XmlDriverEntityManager.class);
public static final String DEFAULT_DRIVER_ENTITY_INFO_FILE_NAME = "driverEntityInfo.xml";
public static final String ELEMENT_NAME_ROOT = "driver-entities";
public static final String ELEMENT_NAME_DRIVER_ENTITY = "driver-entity";
public static final String ELEMENT_NAME_ID = "id";
public static final String ELEMENT_NAME_DRIVER_CLASS_NAME = "driver-class-name";
public static final String ELEMENT_NAME_DISPLAY_NAME = "display-name";
public static final String ELEMENT_NAME_DISPLAY_DESC = "display-desc";
public XmlDriverEntityManager()
{
super();
}
public XmlDriverEntityManager(String rootDirectory)
{
super(rootDirectory, DEFAULT_DRIVER_ENTITY_INFO_FILE_NAME);
}
public XmlDriverEntityManager(File rootDirectory)
{
super(rootDirectory, DEFAULT_DRIVER_ENTITY_INFO_FILE_NAME);
}
@Override
protected List<DriverEntity> readDriverEntities(File driverEntityInfoFile) throws DriverEntityManagerException
{
List<DriverEntity> driverEntities = new ArrayList<DriverEntity>();
DocumentBuilderFactory documentBuilderFactory;
DocumentBuilder documentBuilder;
Document document;
Reader reader = getDriverEntityInfoFileReader();
try
{
documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilder = documentBuilderFactory.newDocumentBuilder();
document = documentBuilder.parse(new InputSource(reader));
NodeList nodeList = document.getElementsByTagName(ELEMENT_NAME_DRIVER_ENTITY);
for (int i = 0; i < nodeList.getLength(); i++)
{
DriverEntity driverEntity = new DriverEntity();
Node node = nodeList.item(i);
NodeList children = node.getChildNodes();
for (int j = 0; j < children.getLength(); j++)
{
Node child = children.item(j);
String nodeName = child.getNodeName();
String nodeContent = child.getTextContent();
if (nodeContent != null)
nodeContent = nodeContent.trim();
if (ELEMENT_NAME_ID.equalsIgnoreCase(nodeName))
driverEntity.setId(nodeContent);
else if (ELEMENT_NAME_DRIVER_CLASS_NAME.equalsIgnoreCase(nodeName))
driverEntity.setDriverClassName(nodeContent);
else if (ELEMENT_NAME_DISPLAY_NAME.equalsIgnoreCase(nodeName))
driverEntity.setDisplayName(nodeContent);
else if (ELEMENT_NAME_DISPLAY_DESC.equalsIgnoreCase(nodeName))
driverEntity.setDisplayDesc(nodeContent);
}
if (driverEntity.getId() != null && !driverEntity.getId().isEmpty()
&& driverEntity.getDriverClassName() != null && !driverEntity.getDriverClassName().isEmpty())
{
removeExists(driverEntities, driverEntity.getId());
driverEntities.add(driverEntity);
if (LOGGER.isDebugEnabled())
LOGGER.debug(
"Read a [" + driverEntity + "] from file [" + driverEntityInfoFile.getPath() + "]");
}
}
}
catch (Exception e)
{
throw new DriverEntityManagerException(e);
}
finally
{
close(reader);
}
return driverEntities;
}
@Override
protected void writeDriverEntities(List<DriverEntity> driverEntities, File driverEntityInfoFile)
throws DriverEntityManagerException
{
Writer writer = getDriverEntityInfoFileWriter();
try
{
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.newDocument();
document.setXmlStandalone(true);
Element root = document.createElement(ELEMENT_NAME_ROOT);
if (driverEntities != null)
{
for (DriverEntity driverEntity : driverEntities)
{
Element driverEntityEle = document.createElement(ELEMENT_NAME_DRIVER_ENTITY);
Element idEle = document.createElement(ELEMENT_NAME_ID);
idEle.setTextContent(driverEntity.getId());
Element driverClassNameEle = document.createElement(ELEMENT_NAME_DRIVER_CLASS_NAME);
driverClassNameEle.setTextContent(driverEntity.getDriverClassName());
Element displayNameEle = document.createElement(ELEMENT_NAME_DISPLAY_NAME);
displayNameEle.setTextContent(driverEntity.getDisplayName());
Element displayDescEle = document.createElement(ELEMENT_NAME_DISPLAY_DESC);
displayDescEle.setTextContent(driverEntity.getDisplayDesc());
driverEntityEle.appendChild(idEle);
driverEntityEle.appendChild(driverClassNameEle);
driverEntityEle.appendChild(displayNameEle);
driverEntityEle.appendChild(displayDescEle);
root.appendChild(driverEntityEle);
}
}
document.appendChild(root);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(new DOMSource(document), new StreamResult(writer));
}
catch (Exception e)
{
throw new DriverEntityManagerException(e);
}
finally
{
close(writer);
}
}
}

View File

@ -0,0 +1,179 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.connection;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.sql.Driver;
import java.sql.DriverManager;
import java.util.Enumeration;
import java.util.List;
import org.datagear.connection.DriverEntity;
import org.datagear.connection.XmlDriverEntityManager;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* {@linkplain XmlDriverEntityManager}单元测试类
*
* @author datagear@163.com
*
*/
public class XmlDriverEntityManagerTest
{
public XmlDriverEntityManagerTest()
{
super();
}
@Before
public void setUp() throws Exception
{
}
@After
public void tearDown() throws Exception
{
}
@Test
public void test() throws Exception
{
XmlDriverEntityManager xmlDriverEntityManager = new XmlDriverEntityManager("target/drivers/");
xmlDriverEntityManager.init();
DriverEntity[] expected = new DriverEntity[4];
DriverEntity driverEntity0 = new DriverEntity("mysql", "com.mysql.Driver");
expected[0] = driverEntity0;
DriverEntity driverEntity1 = new DriverEntity("oracle", "oracle.jdbc.OracleDriver");
expected[1] = driverEntity1;
DriverEntity driverEntity2 = new DriverEntity("my", "my.jdbc.MyDriver");
driverEntity2.setDisplayName("my");
driverEntity2.setDisplayDesc("my-description, used for database <a>, b, c");
expected[2] = driverEntity2;
DriverEntity driverEntity3 = new DriverEntity("my1", "my.jdbc.MyDriver");
driverEntity3.setDisplayDesc("my-description1");
expected[3] = driverEntity3;
xmlDriverEntityManager.add(expected);
List<DriverEntity> actualList = xmlDriverEntityManager.getAll();
DriverEntity[] actual = actualList.toArray(new DriverEntity[actualList.size()]);
Assert.assertArrayEquals(expected, actual);
}
@Test
public void getDriverTest() throws Exception
{
printlnMyContextDrivers();
XmlDriverEntityManager driverEntityManager = new XmlDriverEntityManager("src/test/resources/drivers");
{
Driver driver = driverEntityManager.getDriver(DriverEntity.valueOf("mysql", "com.mysql.jdbc.Driver"));
assertNotNull(driver);
assertEquals("com.mysql.jdbc.Driver", driver.getClass().getName());
}
println();
{
Driver driver = driverEntityManager.getDriver(DriverEntity.valueOf("oracle", "oracle.jdbc.OracleDriver"));
assertNotNull(driver);
assertEquals("oracle.jdbc.OracleDriver", driver.getClass().getName());
}
println();
{
Driver driver = driverEntityManager
.getDriver(DriverEntity.valueOf("mysql-connector-java-5.1.23.jar", "com.mysql.jdbc.Driver"));
assertNotNull(driver);
assertEquals("com.mysql.jdbc.Driver", driver.getClass().getName());
}
println();
{
{
Driver driver = driverEntityManager.getDriver(DriverEntity.valueOf("mixed", "com.mysql.jdbc.Driver"));
assertNotNull(driver);
assertEquals("com.mysql.jdbc.Driver", driver.getClass().getName());
}
println();
{
Driver driver = driverEntityManager
.getDriver(DriverEntity.valueOf("mixed", "oracle.jdbc.OracleDriver"));
assertNotNull(driver);
assertEquals("oracle.jdbc.OracleDriver", driver.getClass().getName());
}
}
{
for (int i = 0; i < 5; i++)
{
println("----" + i + "---");
Driver driver = driverEntityManager.getDriver(DriverEntity.valueOf("mysql", "com.mysql.jdbc.Driver"));
assertNotNull(driver);
assertEquals("com.mysql.jdbc.Driver", driver.getClass().getName());
Thread.sleep(5000);
}
}
println();
driverEntityManager.releaseAllDrivers();
printlnMyContextDrivers();
}
protected static void printlnMyContextDrivers()
{
println("");
println("------------------");
println("Context drivers");
println("------------------");
println("");
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements())
{
Driver ddriver = drivers.nextElement();
ClassLoader classLoader = ddriver.getClass().getClassLoader();
println(ddriver.getClass().getName() + ", class loader=" + classLoader);
}
println("");
println("------------------");
println("");
}
protected static void println()
{
println("");
println("------------------");
println("");
}
protected static void println(Object o)
{
System.out.println(o);
}
}

View File

@ -0,0 +1 @@
abcd

View File

@ -0,0 +1,9 @@
#
# Set root category priority to WARN and its only appender to FILE.
#
log4j.rootLogger=DEBUG,A0
#==============appender 0==========================
log4j.appender.A0=org.apache.log4j.ConsoleAppender
log4j.appender.A0.layout=org.apache.log4j.PatternLayout
log4j.appender.A0.layout.ConversionPattern=[%d{HH:mm:ss,SSS}] [%p] %-x [%C.%M()] %m%n

39
datagear-dbinfo/pom.xml Normal file
View File

@ -0,0 +1,39 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.datagear</groupId>
<artifactId>datagear</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>datagear-dbinfo</artifactId>
<name>datagear-dbinfo</name>
<dependencies>
<dependency>
<groupId>org.datagear</groupId>
<artifactId>datagear-connection</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.24</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.Connection;
import org.datagear.connection.ConnectionSensor;
/**
* 抽象数据库连接敏感的{@linkplain DevotedDatabaseInfoResolver}
* <p>
* 此类可以作为特定数据库{@linkplain DevotedDatabaseInfoResolver}实现类的父类
* </p>
*
* @author datagear@163.com
*
*/
public abstract class AbstractConnectionDevotedDatabaseInfoResolver extends AbstractDevotedDatabaseInfoResolver
{
private ConnectionSensor connectionSensor;
public AbstractConnectionDevotedDatabaseInfoResolver()
{
super();
}
public AbstractConnectionDevotedDatabaseInfoResolver(ConnectionSensor connectionSensor)
{
super();
this.connectionSensor = connectionSensor;
}
public ConnectionSensor getConnectionSensor()
{
return connectionSensor;
}
public void setConnectionSensor(ConnectionSensor connectionSensor)
{
this.connectionSensor = connectionSensor;
}
@Override
public boolean supports(Connection cn)
{
return this.connectionSensor.supports(cn);
}
}

View File

@ -0,0 +1,662 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.datagear.connection.JdbcUtil;
/**
* 抽象{@linkplain DevotedDatabaseInfoResolver}
*
* @author datagear@163.com
*
*/
public abstract class AbstractDevotedDatabaseInfoResolver implements DevotedDatabaseInfoResolver
{
protected static final String[] TABLE_TABLE_TYPES = new String[] { TableInfoResultSetSpec.TABLE_TYPE_TABLE };
public AbstractDevotedDatabaseInfoResolver()
{
super();
}
@Override
public DatabaseInfo getDatabaseInfo(Connection cn) throws DatabaseInfoResolverException
{
try
{
DatabaseMetaData metaData = cn.getMetaData();
DatabaseInfo databaseInfo = new DatabaseInfo();
databaseInfo.setCatalog(cn.getCatalog());
databaseInfo.setSchema(getSchema(cn, metaData));
databaseInfo.setUrl(metaData.getURL());
databaseInfo.setUser(metaData.getUserName());
databaseInfo.setProductName(metaData.getDatabaseProductName());
databaseInfo.setProductVersion(metaData.getDatabaseProductVersion());
databaseInfo.setDriverName(metaData.getDriverName());
databaseInfo.setDriverVersion(metaData.getDriverVersion());
return databaseInfo;
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
}
@Override
public TableInfo[] getTableInfos(Connection cn) throws DatabaseInfoResolverException
{
ResultSet rs = null;
try
{
DatabaseMetaData metaData = cn.getMetaData();
rs = this.getTableInfoResulSet(cn, metaData, getSchema(cn, metaData), null, getTableTypes());
List<TableInfo> tableInfos = getTableInfoResultSetSpec().read(rs);
TableInfo[] array = tableInfos.toArray(new TableInfo[tableInfos.size()]);
postProcessTableInfo(array);
return array;
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
@Override
public TableInfo getRandomTableInfo(Connection cn) throws DatabaseInfoResolverException
{
ResultSet rs = null;
try
{
DatabaseMetaData metaData = cn.getMetaData();
rs = this.getTableInfoResulSet(cn, metaData, getSchema(cn, metaData), null, TABLE_TABLE_TYPES);
List<TableInfo> tableInfos = getTableInfoResultSetSpec().read(rs, 1, 1);
TableInfo[] array = tableInfos.toArray(new TableInfo[tableInfos.size()]);
postProcessTableInfo(array);
return (array.length > 0 ? array[0] : null);
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
@Override
public TableInfo getTableInfo(Connection cn, String tableName) throws DatabaseInfoResolverException
{
ResultSet rs = null;
try
{
DatabaseMetaData metaData = cn.getMetaData();
rs = getTableInfoResulSet(cn, metaData, getSchema(cn, metaData), tableName, getTableTypes());
List<TableInfo> tableInfos = getTableInfoResultSetSpec().read(rs);
if (tableInfos == null || tableInfos.isEmpty())
throw new TableNotExistsException(tableName, "Table [" + tableName + "] is not exists");
TableInfo[] array = tableInfos.toArray(new TableInfo[tableInfos.size()]);
postProcessTableInfo(array);
return array[0];
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
@Override
public EntireTableInfo getEntireTableInfo(Connection cn, String tableName) throws DatabaseInfoResolverException
{
EntireTableInfo entireTableInfo = new EntireTableInfo();
TableInfo tableInfo = getTableInfo(cn, tableName);
ColumnInfo[] columnInfos = getColumnInfos(cn, tableName);
String[] primaryKeyColumnNames = getPrimaryKeyColumnNames(cn, tableName);
String[][] uniqueKeyColumnNames = getUniqueKeyColumnNames(cn, tableName);
ImportedKeyInfo[] importedKeyInfos = getImportedKeyInfos(cn, tableName);
ExportedKeyInfo[] exportedKeyInfos = getExportedKeyInfos(cn, tableName);
entireTableInfo.setTableInfo(tableInfo);
entireTableInfo.setColumnInfos(columnInfos);
entireTableInfo.setPrimaryKeyColumnNames(primaryKeyColumnNames);
entireTableInfo.setUniqueKeyColumnNames(uniqueKeyColumnNames);
entireTableInfo.setImportedKeyInfos(importedKeyInfos);
entireTableInfo.setExportedKeyInfos(exportedKeyInfos);
return entireTableInfo;
}
@Override
public ColumnInfo[] getColumnInfos(Connection cn, String tableName) throws DatabaseInfoResolverException
{
ResultSet rs = null;
try
{
DatabaseMetaData metaData = cn.getMetaData();
rs = getColumnInfoResulSet(cn, metaData, tableName);
List<ColumnInfo> columnInfos = getColumnInfoResultSetSpec().read(rs);
ColumnInfo[] array = columnInfos.toArray(new ColumnInfo[columnInfos.size()]);
postProcessColumnInfo(array);
return array;
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
@Override
public ColumnInfo getRandomColumnInfo(Connection cn, String tableName) throws DatabaseInfoResolverException
{
ResultSet rs = null;
try
{
DatabaseMetaData metaData = cn.getMetaData();
rs = getColumnInfoResulSet(cn, metaData, tableName);
List<ColumnInfo> columnInfos = getColumnInfoResultSetSpec().read(rs, 1, 1);
ColumnInfo[] array = columnInfos.toArray(new ColumnInfo[columnInfos.size()]);
postProcessColumnInfo(array);
return (array.length > 0 ? array[0] : null);
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
@Override
public String[] getPrimaryKeyColumnNames(Connection cn, String tableName) throws DatabaseInfoResolverException
{
ResultSet rs = null;
try
{
DatabaseMetaData metaData = cn.getMetaData();
rs = getPrimaryKeyResulSet(cn, metaData, tableName);
List<PrimaryKeyInfo> primaryKeyInfos = getPrimaryKeyInfoResultSetSpec().read(rs);
String[] names = new String[primaryKeyInfos.size()];
for (int i = 0, len = primaryKeyInfos.size(); i < len; i++)
{
names[i] = primaryKeyInfos.get(i).getColumnName();
}
return names;
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
@Override
public String[][] getUniqueKeyColumnNames(Connection cn, String tableName) throws DatabaseInfoResolverException
{
ResultSet rs = null;
List<String> uniqueKeyNames = new ArrayList<String>();
List<List<String>> uniqueKeyColumnNames = new ArrayList<List<String>>();
try
{
DatabaseMetaData metaData = cn.getMetaData();
rs = getUniqueKeyResulSet(cn, metaData, tableName);
List<UniqueKeyInfo> uniqueKeyInfos = getUniqueKeyInfoResultSetSpec().read(rs);
for (UniqueKeyInfo uniqueKeyInfo : uniqueKeyInfos)
{
String uniqueKeyName = uniqueKeyInfo.getKeyName();
String columnName = uniqueKeyInfo.getColumnName();
List<String> uniqueKeyColumnName = null;
int myIndex = uniqueKeyNames.indexOf(uniqueKeyName);
if (myIndex < 0)
{
uniqueKeyNames.add(uniqueKeyName);
uniqueKeyColumnName = new ArrayList<String>();
uniqueKeyColumnNames.add(uniqueKeyColumnName);
}
else
uniqueKeyColumnName = uniqueKeyColumnNames.get(myIndex);
uniqueKeyColumnName.add(columnName);
}
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
int size = uniqueKeyColumnNames.size();
String[][] uniqueKeyColumnNamesAry = new String[size][];
for (int i = 0; i < size; i++)
{
List<String> myUniqueKeyColumnNames = uniqueKeyColumnNames.get(i);
uniqueKeyColumnNamesAry[i] = myUniqueKeyColumnNames.toArray(new String[myUniqueKeyColumnNames.size()]);
}
return uniqueKeyColumnNamesAry;
}
@Override
public ImportedKeyInfo[] getImportedKeyInfos(Connection cn, String tableName) throws DatabaseInfoResolverException
{
ResultSet rs = null;
try
{
DatabaseMetaData metaData = cn.getMetaData();
rs = getImportedKeyResulSet(cn, metaData, tableName);
List<ImportedKeyInfo> importedKeyInfos = getImportedKeyInfoResultSetSpec().read(rs);
ImportedKeyInfo[] array = importedKeyInfos.toArray(new ImportedKeyInfo[importedKeyInfos.size()]);
postProcessImportedKeyInfo(array);
return array;
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
@Override
public ExportedKeyInfo[] getExportedKeyInfos(Connection cn, String tableName) throws DatabaseInfoResolverException
{
ResultSet rs = null;
try
{
DatabaseMetaData metaData = cn.getMetaData();
rs = getExportedKeyResulSet(cn, metaData, tableName);
List<ExportedKeyInfo> exportedKeyInfos = getExportedKeyInfoResultSetSpec().read(rs);
ExportedKeyInfo[] array = exportedKeyInfos.toArray(new ExportedKeyInfo[exportedKeyInfos.size()]);
postProcessExportedKeyInfo(array);
return array;
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
/**
* 构建{@linkplain TableInfo}结果集
*
* @param cn
* @param databaseMetaData
* @param schema
* @param tableNamePattern
* @param tableTypes
* @return
* @throws SQLException
*/
protected ResultSet getTableInfoResulSet(Connection cn, DatabaseMetaData databaseMetaData, String schema,
String tableNamePattern, String[] tableTypes) throws SQLException
{
if (tableNamePattern == null || tableNamePattern.isEmpty())
tableNamePattern = "%";
return databaseMetaData.getTables(cn.getCatalog(), schema, tableNamePattern, tableTypes);
}
/**
* 构建{@linkplain ColumnInfo}结果集
*
* @param cn
* @param databaseMetaData
* @param tableName
* @return
* @throws SQLException
*/
protected ResultSet getColumnInfoResulSet(Connection cn, DatabaseMetaData databaseMetaData, String tableName)
throws SQLException
{
return databaseMetaData.getColumns(cn.getCatalog(), getSchema(cn, databaseMetaData), tableName, "%");
}
/**
* 构建主键结果集
*
* @param cn
* @param databaseMetaData
* @param tableName
* @return
* @throws SQLException
*/
protected ResultSet getPrimaryKeyResulSet(Connection cn, DatabaseMetaData databaseMetaData, String tableName)
throws SQLException
{
return databaseMetaData.getPrimaryKeys(cn.getCatalog(), getSchema(cn, databaseMetaData), tableName);
}
/**
* 构建唯一键结果集
*
* @param cn
* @param databaseMetaData
* @param tableName
* @return
* @throws SQLException
*/
protected ResultSet getUniqueKeyResulSet(Connection cn, DatabaseMetaData databaseMetaData, String tableName)
throws SQLException
{
return databaseMetaData.getIndexInfo(cn.getCatalog(), getSchema(cn, databaseMetaData), tableName, true, false);
}
/**
* 构建{@linkplain ImportedKeyInfo}结果集
*
* @param cn
* @param databaseMetaData
* @param tableName
* @return
* @throws SQLException
*/
protected ResultSet getImportedKeyResulSet(Connection cn, DatabaseMetaData databaseMetaData, String tableName)
throws SQLException
{
return databaseMetaData.getImportedKeys(cn.getCatalog(), getSchema(cn, databaseMetaData), tableName);
}
/**
* 构建{@linkplain ExportedKeyInfo}结果集
*
* @param cn
* @param databaseMetaData
* @param tableName
* @return
* @throws SQLException
*/
protected ResultSet getExportedKeyResulSet(Connection cn, DatabaseMetaData databaseMetaData, String tableName)
throws SQLException
{
return databaseMetaData.getExportedKeys(cn.getCatalog(), getSchema(cn, databaseMetaData), tableName);
}
/**
* 获取{@linkplain TableInfo}{@linkplain ResultSetSpec}
*
* @return
*/
protected ResultSetSpec<TableInfo> getTableInfoResultSetSpec()
{
return new TableInfoResultSetSpec();
}
/**
* 获取{@linkplain ColumnInfo}{@linkplain ResultSetSpec}
*
* @return
*/
protected ResultSetSpec<ColumnInfo> getColumnInfoResultSetSpec()
{
return new ColumnInfoResultSetSpec();
}
/**
* 获取{@linkplain PrimaryKeyInfo}{@linkplain ResultSetSpec}
*
* @return
*/
protected ResultSetSpec<PrimaryKeyInfo> getPrimaryKeyInfoResultSetSpec()
{
return new PrimaryKeyInfoResultSetSpec();
}
/**
* 获取{@linkplain UniqueKeyInfo}{@linkplain ResultSetSpec}
*
* @return
*/
protected ResultSetSpec<UniqueKeyInfo> getUniqueKeyInfoResultSetSpec()
{
return new UniqueKeyInfoResultSetSpec();
}
/**
* 获取{@linkplain ImportedKeyInfo}{@linkplain ResultSetSpec}
*
* @return
*/
protected ResultSetSpec<ImportedKeyInfo> getImportedKeyInfoResultSetSpec()
{
return new ImportedKeyInfoResultSetSpec();
}
/**
* 获取{@linkplain ExportedKeyInfo}{@linkplain ResultSetSpec}
*
* @return
*/
protected ResultSetSpec<ExportedKeyInfo> getExportedKeyInfoResultSetSpec()
{
return new ExportedKeyInfoResultSetSpec();
}
/**
* 获取表类型数组
*
* @return
*/
protected String[] getTableTypes()
{
return TABLE_TABLE_TYPES;
}
/**
* 后置处理{@link TableInfo}
* <p>
* 子类可以重写此方法对{@linkplain TableInfo}进行特殊后置处理
* </p>
*
* @param tableInfo
*/
protected void postProcessTableInfo(TableInfo[] tableInfos)
{
}
/**
* 后置处理{@link ColumnInfo}
* <p>
* 子类可以重写此方法对{@linkplain ColumnInfo}进行特殊后置处理
* </p>
*
* @param columnInfos
*/
protected void postProcessColumnInfo(ColumnInfo[] columnInfos)
{
}
/**
* 后置处理{@link ImportedKeyInfo}
* <p>
* 子类可以重写此方法对{@linkplain ImportedKeyInfo}进行特殊后置处理
* </p>
*
* @param importedKeyInfos
*/
protected void postProcessImportedKeyInfo(ImportedKeyInfo[] importedKeyInfos)
{
}
/**
* 后置处理{@link ExportedKeyInfo}
* <p>
* 子类可以重写此方法对{@linkplain ExportedKeyInfo}进行特殊后置处理
* </p>
*
* @param exportedKeyInfos
*/
protected void postProcessExportedKeyInfo(ExportedKeyInfo[] exportedKeyInfos)
{
}
/**
* 获取指定连接的schema
*
* @param cn
* @param databaseMetaData
* @return
*/
protected String getSchema(Connection cn, DatabaseMetaData databaseMetaData)
{
// 在JDBC4.0JDK1.6中需要将其设置为null才符合DatabaseMetaData.getTables(...)等接口的参数要求
String schema = null;
// JDBC4.1JDK1.7才有Connection.getSchema()接口为了兼容性本应用采用了JDBC4.0JDK1.6
// 这里为了能支持JDBC4.1的的驱动先采用反射方式获取
if (GET_SCHEMA_METHOD_JDBC41 != null)
{
try
{
schema = (String) GET_SCHEMA_METHOD_JDBC41.invoke(cn, new Object[0]);
}
catch (Exception e)
{
}
}
// 测试使用连接用户名是否可行
if (schema == null)
{
ResultSet rs = null;
try
{
String dbUserName = databaseMetaData.getUserName();
rs = getTableInfoResulSet(cn, databaseMetaData, dbUserName, null, getTableTypes());
if (rs.next())
schema = dbUserName;
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
return schema;
}
protected static Method GET_SCHEMA_METHOD_JDBC41 = null;
static
{
Method[] methods = Connection.class.getMethods();
for (Method method : methods)
{
if ("getSchema".equals(method.getName()))
{
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes == null || parameterTypes.length == 0)
{
GET_SCHEMA_METHOD_JDBC41 = method;
break;
}
}
}
}
}

View File

@ -0,0 +1,159 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.DatabaseMetaData;
/**
* 表的列信息
* <p>
* 类结构参考
* {@linkplain DatabaseMetaData#getColumns(String, String, String, String)}
* 返回结果
* </p>
*
* @author datagear@163.com
*
*/
public class ColumnInfo extends ResultSetSpecBean
{
private static final long serialVersionUID = 1L;
/** 名称 */
private String name;
/** JDBC类型对应java.sql.Types中的值 */
private int type;
/** 数据源依赖的类型名称,参考"TYPE_NAME"说明 */
private String typeName;
/** 列大小,字符串长度或者数值总长度 */
private int size;
/** 小数部分的位数,如果不是小数,值为-1 */
private int decimalDigits;
/** 是否允许为null */
private boolean nullable;
/** 描述 */
private String comment;
/** 默认值 */
private String defaultValue;
/** 是否自增长 */
private boolean autoincrement;
public ColumnInfo()
{
super();
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getType()
{
return type;
}
public void setType(int type)
{
this.type = type;
}
public String getTypeName()
{
return typeName;
}
public void setTypeName(String typeName)
{
this.typeName = typeName;
}
/**
* 获取列大小返回{@code <= 0}表示无此值
*
* @return
*/
public int getSize()
{
return size;
}
public void setSize(int size)
{
this.size = size;
}
public int getDecimalDigits()
{
return decimalDigits;
}
public void setDecimalDigits(int decimalDigits)
{
this.decimalDigits = decimalDigits;
}
public boolean isNullable()
{
return nullable;
}
public void setNullable(boolean nullable)
{
this.nullable = nullable;
}
public String getComment()
{
return comment;
}
public void setComment(String comment)
{
this.comment = comment;
}
public String getDefaultValue()
{
return defaultValue;
}
public void setDefaultValue(String defaultValue)
{
this.defaultValue = defaultValue;
}
public boolean isAutoincrement()
{
return autoincrement;
}
public void setAutoincrement(boolean autoincrement)
{
this.autoincrement = autoincrement;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", type=" + type + ", typeName=" + typeName + ", size="
+ size + ", decimalDigits=" + decimalDigits + ", nullable=" + nullable + ", comment=" + comment
+ ", defaultValue=" + defaultValue + ", autoincrement=" + autoincrement + "]";
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.DatabaseMetaData;
/**
* {@linkplain ColumnInfo}结果集规范
*
* @author datagear@163.com
*
*/
public class ColumnInfoResultSetSpec extends ResultSetSpec<ColumnInfo>
{
public static final Converter<Integer, Boolean> NULLABLE_CONVERTER = new Converter<Integer, Boolean>()
{
@Override
public Boolean convert(Integer s) throws ResultSetIncompatibleException
{
if (DatabaseMetaData.columnNoNulls == s)
return false;
else
return true;
}
};
public static final Converter<String, Boolean> IS_AUTOINCREMENT_CONVERTER = new Converter<String, Boolean>()
{
@Override
public Boolean convert(String s) throws ResultSetIncompatibleException
{
return "yes".equalsIgnoreCase(s);
}
};
public static final RsColumnSpec<?, ?>[] RS_COLUMN_SPECS = new RsColumnSpec[] {
new RsColumnSpec<String, String>("COLUMN_NAME", String.class, true, false, "name"),
new RsColumnSpec<Integer, Integer>("DATA_TYPE", Integer.class, true, false, "type"),
new RsColumnSpec<String, String>("TYPE_NAME", String.class, true, false, "typeName"),
new RsColumnSpec<Integer, Integer>("COLUMN_SIZE", Integer.class, false, true, 0, "size"),
new RsColumnSpec<Integer, Integer>("DECIMAL_DIGITS", Integer.class, false, true, 0, "decimalDigits"),
new RsColumnSpec<Integer, Boolean>("NULLABLE", Integer.class, false, true, "nullable", NULLABLE_CONVERTER),
new RsColumnSpec<String, String>("REMARKS", String.class, false, true, "", "comment"),
new RsColumnSpec<String, String>("COLUMN_DEF", String.class, false, true, "defaultValue"),
new RsColumnSpec<String, Boolean>("IS_AUTOINCREMENT", String.class, false, true, "autoincrement",
IS_AUTOINCREMENT_CONVERTER) };
public ColumnInfoResultSetSpec()
{
super();
}
@Override
protected Class<ColumnInfo> getRowType()
{
return ColumnInfo.class;
}
@Override
protected RsColumnSpec<?, ?>[] getRsColumnSpecs()
{
return RS_COLUMN_SPECS;
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
/**
* 数据库信息
* <p>
* 类结构参考{@linkplain DatabaseMetaData}{@linkplain Connection}相关方法
* </p>
*
* @author datagear@163.com
*
*/
public class DatabaseInfo implements Serializable
{
private static final long serialVersionUID = 1L;
private String url;
private String user;
private String productName;
private String productVersion;
private String catalog;
private String schema;
private String driverName;
private String driverVersion;
public DatabaseInfo()
{
super();
}
public String getUrl()
{
return url;
}
public void setUrl(String url)
{
this.url = url;
}
public String getUser()
{
return user;
}
public void setUser(String user)
{
this.user = user;
}
public String getProductName()
{
return productName;
}
public void setProductName(String productName)
{
this.productName = productName;
}
public String getProductVersion()
{
return productVersion;
}
public void setProductVersion(String productVersion)
{
this.productVersion = productVersion;
}
public String getCatalog()
{
return catalog;
}
public void setCatalog(String catalog)
{
this.catalog = catalog;
}
public String getSchema()
{
return schema;
}
public void setSchema(String schema)
{
this.schema = schema;
}
public String getDriverName()
{
return driverName;
}
public void setDriverName(String driverName)
{
this.driverName = driverName;
}
public String getDriverVersion()
{
return driverVersion;
}
public void setDriverVersion(String driverVersion)
{
this.driverVersion = driverVersion;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [url=" + url + ", user=" + user + ", productName=" + productName
+ ", productVersion=" + productVersion + ", catalog=" + catalog + ", schema=" + schema + ", driverName="
+ driverName + ", driverVersion=" + driverVersion + "]";
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.Connection;
/**
* 数据库信息解析器
*
* @author datagear@163.com
*
*/
public interface DatabaseInfoResolver
{
/**
* 获取{@linkplain DatabaseInfo}
*
* @param cn
* @return
* @throws DatabaseInfoResolverException
*/
DatabaseInfo getDatabaseInfo(Connection cn) throws DatabaseInfoResolverException;
/**
* 获取所有{@linkplain TableInfo}
*
* @param cn
* @return
* @throws DatabaseInfoResolverException
*/
TableInfo[] getTableInfos(Connection cn) throws DatabaseInfoResolverException;
/**
* 获取一个随机表信息
* <p>
* 返回{@linkplain TableInfo#getType()}{@linkplain TableType#TABLE}
* </p>
* <p>
* 如果没有任何表将返回{@code null}
* </p>
*
* @param cn
* @return
* @throws DatabaseInfoResolverException
*/
TableInfo getRandomTableInfo(Connection cn) throws DatabaseInfoResolverException;
/**
* 获取指定名称的{@linkplain TableInfo}
*
* @param cn
* @param tableName
* @return
* @throws DatabaseInfoResolverException
*/
TableInfo getTableInfo(Connection cn, String tableName) throws DatabaseInfoResolverException;
/**
* 获取{@linkplain EntireTableInfo}
*
* @param cn
* @param tableName
* @return
* @throws DatabaseInfoResolverException
*/
EntireTableInfo getEntireTableInfo(Connection cn, String tableName) throws DatabaseInfoResolverException;
/**
* 获取指定表的{@linkplain ColumnInfo}
*
* @param cn
* @param tableName
* @return
* @throws DatabaseInfoResolverException
*/
ColumnInfo[] getColumnInfos(Connection cn, String tableName) throws DatabaseInfoResolverException;
/**
* 获取指定表的一个随机{@linkplain ColumnInfo}
* <p>
* 如果表没有任何字段将返回{@code null}
* </p>
*
* @param cn
* @param tableName
* @return
* @throws DatabaseInfoResolverException
*/
ColumnInfo getRandomColumnInfo(Connection cn, String tableName) throws DatabaseInfoResolverException;
/**
* 获取指定表的主键列名称
*
* @param cn
* @param tableName
* @return
* @throws DatabaseInfoResolverException
*/
String[] getPrimaryKeyColumnNames(Connection cn, String tableName) throws DatabaseInfoResolverException;
/**
* 获取指定表的唯一键列名称
*
* @param cn
* @param tableName
* @return
* @throws DatabaseInfoResolverException
*/
String[][] getUniqueKeyColumnNames(Connection cn, String tableName) throws DatabaseInfoResolverException;
/**
* 获取指定表的{@linkplain ImportedKeyInfo}
*
* @param cn
* @param tableName
* @return
* @throws DatabaseInfoResolverException
*/
ImportedKeyInfo[] getImportedKeyInfos(Connection cn, String tableName) throws DatabaseInfoResolverException;
/**
* 获取指定表的{@linkplain ExportedKeyInfo}
*
* @param cn
* @param tableName
* @return
* @throws DatabaseInfoResolverException
*/
ExportedKeyInfo[] getExportedKeyInfos(Connection cn, String tableName) throws DatabaseInfoResolverException;
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
/**
* {@linkplain DatabaseInfoResolver}异常
*
* @author datagear@163.com
*
*/
public class DatabaseInfoResolverException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public DatabaseInfoResolverException()
{
super();
}
public DatabaseInfoResolverException(String message)
{
super(message);
}
public DatabaseInfoResolverException(Throwable cause)
{
super(cause);
}
public DatabaseInfoResolverException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -0,0 +1,580 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* {@linkplain DatabaseMetaData}信息打印类
*
* @author datagear@163.com
*
*/
public class DatabaseMetaDataPrinter extends AbstractDevotedDatabaseInfoResolver
{
protected static final List<ActionInfo> ACTION_INFOS = new ArrayList<ActionInfo>();
public static void registerActionInfo(String actionMethodName, String... actionMethodArgNames)
{
ActionInfo actionInfo = new ActionInfo(actionMethodName, actionMethodArgNames);
ACTION_INFOS.add(actionInfo);
}
static
{
registerActionInfo("print_getCatalog");
registerActionInfo("print_getSchema");
registerActionInfo("print_getCatalogs");
registerActionInfo("print_getSchemas");
registerActionInfo("print_getUserName");
registerActionInfo("print_getTableTypes");
registerActionInfo("print_getTables", "catalog", "schema");
registerActionInfo("print_getPrimaryKeys", "catalog", "schema", "table");
registerActionInfo("print_getColumn", "catalog", "schema", "table");
registerActionInfo("print_getExportedKeys", "catalog", "schema", "table");
registerActionInfo("print_getImportedKeys", "catalog", "schema", "table");
registerActionInfo("print_getUniqueKeys", "catalog", "schema", "table");
}
private String driverClassName;
private String url;
private String user;
private String password;
public DatabaseMetaDataPrinter()
{
super();
}
public DatabaseMetaDataPrinter(String driverClassName, String url, String user, String password)
{
super();
this.driverClassName = driverClassName;
this.url = url;
this.user = user;
this.password = password;
}
public String getDriverClassName()
{
return driverClassName;
}
public void setDriverClassName(String driverClassName)
{
this.driverClassName = driverClassName;
}
public String getUrl()
{
return url;
}
public void setUrl(String url)
{
this.url = url;
}
public String getUser()
{
return user;
}
public void setUser(String user)
{
this.user = user;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
@Override
public boolean supports(Connection cn)
{
return true;
}
public void print_getCatalog() throws Exception
{
Connection cn = getConnection();
try
{
printlnData("getCatalog", cn.getCatalog());
}
finally
{
cn.close();
}
}
public void print_getSchema() throws Exception
{
Connection cn = getConnection();
try
{
String schema = getSchema(cn, cn.getMetaData());
printlnData("getSchema", schema);
}
finally
{
cn.close();
}
}
public void print_getCatalogs() throws Exception
{
Connection cn = getConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getCatalogs();
printlnData("getCatalogs", rs);
}
finally
{
cn.close();
}
}
public void print_getSchemas() throws Exception
{
Connection cn = getConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getSchemas();
printlnData("getSchemas", rs);
}
finally
{
cn.close();
}
}
public void print_getUserName() throws Exception
{
Connection cn = getConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
printlnData("getUserName", metaData.getUserName());
}
finally
{
cn.close();
}
}
public void print_getTableTypes() throws Exception
{
Connection cn = getConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getTableTypes();
printlnData("getTableTypes", rs);
}
finally
{
cn.close();
}
}
public void print_getTables(String catalog, String schema) throws Exception
{
Connection cn = getConnection();
try
{
catalog = getCatalog(cn, catalog);
schema = getSchema(cn, schema);
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getTables(catalog, schema, "%", new String[] { "TABLE" });
printlnData("getTables", rs);
}
finally
{
cn.close();
}
}
public void print_getPrimaryKeys(String catalog, String schema, String table) throws Exception
{
Connection cn = getConnection();
try
{
catalog = getCatalog(cn, catalog);
schema = getSchema(cn, schema);
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getPrimaryKeys(catalog, schema, table);
printlnData("getPrimaryKeys", rs);
}
finally
{
cn.close();
}
}
public void print_getColumn(String catalog, String schema, String table) throws Exception
{
Connection cn = getConnection();
try
{
catalog = getCatalog(cn, catalog);
schema = getSchema(cn, schema);
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getColumns(catalog, schema, table, "%");
printlnData("getColumns", rs);
}
finally
{
cn.close();
}
}
public void print_getExportedKeys(String catalog, String schema, String table) throws Exception
{
Connection cn = getConnection();
try
{
catalog = getCatalog(cn, catalog);
schema = getSchema(cn, schema);
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getExportedKeys(catalog, schema, table);
printlnData("getExportedKeys", rs);
}
finally
{
cn.close();
}
}
public void print_getImportedKeys(String catalog, String schema, String table) throws Exception
{
Connection cn = getConnection();
try
{
catalog = getCatalog(cn, catalog);
schema = getSchema(cn, schema);
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getImportedKeys(catalog, schema, table);
printlnData("getImportedKeys", rs);
}
finally
{
cn.close();
}
}
public void print_getUniqueKeys(String catalog, String schema, String table) throws Exception
{
Connection cn = getConnection();
try
{
catalog = getCatalog(cn, catalog);
schema = getSchema(cn, schema);
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getIndexInfo(catalog, schema, table, true, false);
printlnData("getUniqueKeys", rs);
}
finally
{
cn.close();
}
}
protected String getCatalog(Connection cn, String catalog) throws Exception
{
if (catalog == null || catalog.isEmpty())
catalog = cn.getCatalog();
return catalog;
}
protected String getSchema(Connection cn, String schema) throws Exception
{
if (schema == null || schema.isEmpty())
schema = getSchema(cn, cn.getMetaData());
return schema;
}
protected Connection getConnection() throws Exception
{
return DriverManager.getConnection(this.url, this.user, this.password);
}
protected void printlnData(String label, Object data) throws Exception
{
println("");
println("========================================");
println("==" + label);
println("========================================");
if (data instanceof ResultSet)
printlnResultSet(label, (ResultSet) data);
else
println(data);
println("========================================");
println("==" + label);
println("========================================");
}
/**
* 打印{@linkplain ResultSet}
*
* @param label
* @param rs
* @throws SQLException
*/
protected void printlnResultSet(String label, ResultSet rs) throws SQLException
{
ResultSetMetaData rsMeta = rs.getMetaData();
int colCount = rsMeta.getColumnCount();
for (int i = 1; i <= colCount; i++)
{
if (i > 1)
print(", ");
print(rsMeta.getColumnLabel(i));
}
println("");
println("------------------------------");
int count = 0;
while (rs.next())
{
for (int i = 1; i <= colCount; i++)
{
if (i > 1)
print(", ");
print(rs.getObject(i));
}
println("");
count++;
}
println("------------------------------");
println("Total : " + count);
}
protected static void println(Object o)
{
System.out.println((o == null ? "null" : o.toString()));
}
protected static void print(Object o)
{
System.out.print((o == null ? "null" : o.toString()));
}
public static void main(String[] args) throws Exception
{
String driverClassName = null;
String url = null;
String user = null;
String password = null;
Scanner scanner = new Scanner(System.in);
while (driverClassName == null)
{
println("Please input the jdbc driver class name :");
driverClassName = scanner.nextLine();
try
{
Class.forName(driverClassName);
}
catch (Exception e)
{
println(e);
driverClassName = null;
}
}
println("Please input the jdbc connection url :");
url = scanner.nextLine();
println("Please input the jdbc connection user :");
user = scanner.nextLine();
println("Please input the jdbc connection password :");
password = scanner.nextLine();
DatabaseMetaDataPrinter printer = new DatabaseMetaDataPrinter(driverClassName, url, user, password);
String actionIndexStr = null;
while (!"exit".equalsIgnoreCase(actionIndexStr))
{
println("");
println("=======================");
for (int i = 0; i < ACTION_INFOS.size(); i++)
{
ActionInfo actionInfo = ACTION_INFOS.get(i);
println(i + " : " + actionInfo.getActionMethodName());
}
println("exit : exit");
println("=======================");
println("");
println("Please input the action index:");
actionIndexStr = scanner.nextLine();
int actionIndex = -1;
try
{
actionIndex = Integer.parseInt(actionIndexStr);
}
catch (Exception e)
{
actionIndex = -1;
}
if (actionIndex < 0 || actionIndex >= ACTION_INFOS.size())
continue;
ActionInfo actionInfo = ACTION_INFOS.get(actionIndex);
String[] actionMethodArgNames = actionInfo.getActionMethodArgNames();
String[] methodArgs = new String[actionMethodArgNames.length];
for (int i = 0; i < actionMethodArgNames.length; i++)
{
println("Please input [" + actionInfo.getActionMethodName() + "] method arg [" + actionMethodArgNames[i]
+ "] value ([null] for null value) :");
methodArgs[i] = scanner.nextLine();
if (methodArgs[i].equalsIgnoreCase("null"))
methodArgs[i] = null;
}
try
{
Method method = getMethodByName(actionInfo.getActionMethodName());
method.invoke(printer, (Object[]) methodArgs);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
protected static Method getMethodByName(String methodName)
{
Method[] methods = DatabaseMetaDataPrinter.class.getMethods();
for (Method method : methods)
{
if (method.getName().equals(methodName))
return method;
}
throw new IllegalArgumentException(
"No method named [" + methodName + "] in class [" + DatabaseMetaDataPrinter.class.getName() + "]");
}
protected static class ActionInfo
{
private String actionMethodName;
private String[] actionMethodArgNames;
public ActionInfo()
{
super();
}
public ActionInfo(String actionMethodName, String[] actionMethodArgNames)
{
super();
this.actionMethodName = actionMethodName;
this.actionMethodArgNames = actionMethodArgNames;
}
public String getActionMethodName()
{
return actionMethodName;
}
public void setActionMethodName(String actionMethodName)
{
this.actionMethodName = actionMethodName;
}
public String[] getActionMethodArgNames()
{
return actionMethodArgNames;
}
public void setActionMethodArgNames(String[] actionMethodArgNames)
{
this.actionMethodArgNames = actionMethodArgNames;
}
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import org.datagear.connection.ConnectionSensor;
/**
* 专职{@linkplain DatabaseInfoResolver}
* <p>
* 此类继承自{@linkplain DatabaseInfoResolver}的所有方法仅在{@linkplain #supports(java.sql.Connection)}返回{@code true}时可用
* </p>
*
* @author datagear@163.com
*
*/
public interface DevotedDatabaseInfoResolver extends DatabaseInfoResolver, ConnectionSensor
{
}

View File

@ -0,0 +1,159 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
/**
* 完整表信息
*
* @author datagear@163.com
*
*/
public class EntireTableInfo
{
private TableInfo tableInfo;
private ColumnInfo[] columnInfos;
private String[] primaryKeyColumnNames;
private String[][] uniqueKeyColumnNames;
private ImportedKeyInfo[] importedKeyInfos;
private ExportedKeyInfo[] exportedKeyInfos;
public EntireTableInfo()
{
super();
}
public EntireTableInfo(TableInfo tableInfo)
{
super();
this.tableInfo = tableInfo;
}
public EntireTableInfo(TableInfo tableInfo, ColumnInfo[] columnInfos, String[] primaryKeyColumnNames,
String[][] uniqueKeyColumnNames, ImportedKeyInfo[] importedKeyInfos, ExportedKeyInfo[] exportedKeyInfos)
{
super();
this.tableInfo = tableInfo;
this.columnInfos = columnInfos;
this.primaryKeyColumnNames = primaryKeyColumnNames;
this.uniqueKeyColumnNames = uniqueKeyColumnNames;
this.importedKeyInfos = importedKeyInfos;
this.exportedKeyInfos = exportedKeyInfos;
}
public TableInfo getTableInfo()
{
return tableInfo;
}
public void setTableInfo(TableInfo tableInfo)
{
this.tableInfo = tableInfo;
}
public ColumnInfo[] getColumnInfos()
{
return columnInfos;
}
public void setColumnInfos(ColumnInfo[] columnInfos)
{
this.columnInfos = columnInfos;
}
public String[] getPrimaryKeyColumnNames()
{
return primaryKeyColumnNames;
}
public void setPrimaryKeyColumnNames(String[] primaryKeyColumnNames)
{
this.primaryKeyColumnNames = primaryKeyColumnNames;
}
public String[][] getUniqueKeyColumnNames()
{
return uniqueKeyColumnNames;
}
public void setUniqueKeyColumnNames(String[][] uniqueKeyColumnNames)
{
this.uniqueKeyColumnNames = uniqueKeyColumnNames;
}
public ImportedKeyInfo[] getImportedKeyInfos()
{
return importedKeyInfos;
}
public void setImportedKeyInfos(ImportedKeyInfo[] importedKeyInfos)
{
this.importedKeyInfos = importedKeyInfos;
}
public ExportedKeyInfo[] getExportedKeyInfos()
{
return exportedKeyInfos;
}
public void setExportedKeyInfos(ExportedKeyInfo[] exportedKeyInfos)
{
this.exportedKeyInfos = exportedKeyInfos;
}
/**
* 是否有列
*
* @return
*/
public boolean hasColumn()
{
return (this.columnInfos != null && this.columnInfos.length != 0);
}
/**
* 是否有主键
*
* @return
*/
public boolean hasPrimaryKey()
{
return (this.primaryKeyColumnNames != null && this.primaryKeyColumnNames.length != 0);
}
/**
* 是否有唯一键
*
* @return
*/
public boolean hasUniqueKey()
{
return (this.uniqueKeyColumnNames != null && this.uniqueKeyColumnNames.length != 0);
}
/**
* 是否有{@linkplain ImportedKeyInfo}
*
* @return
*/
public boolean hasImportedKey()
{
return (this.importedKeyInfos != null && this.importedKeyInfos.length != 0);
}
/**
* 是否有{@linkplain ExportedKeyInfo}
*
* @return
*/
public boolean hasExportedKey()
{
return (this.exportedKeyInfos != null && this.exportedKeyInfos.length != 0);
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.DatabaseMetaData;
/**
* 表的导出键列信息
* <p>
* 类结构参考{@linkplain DatabaseMetaData#getExportedKeys(String, String, String)}
* 返回结果
* </p>
*
* @author datagear@163.com
*
*/
public class ExportedKeyInfo extends ResultSetSpecBean
{
private static final long serialVersionUID = 1L;
private String pkColumnName;
private String fkTableName;
private String fkColumnName;
private int keySeq;
private ImportedKeyRule updateRule;
private ImportedKeyRule deleteRule;
private String fkName;
private String pkName;
public ExportedKeyInfo()
{
super();
}
public String getPkColumnName()
{
return pkColumnName;
}
public void setPkColumnName(String pkColumnName)
{
this.pkColumnName = pkColumnName;
}
public String getFkTableName()
{
return fkTableName;
}
public void setFkTableName(String fkTableName)
{
this.fkTableName = fkTableName;
}
public String getFkColumnName()
{
return fkColumnName;
}
public void setFkColumnName(String fkColumnName)
{
this.fkColumnName = fkColumnName;
}
public int getKeySeq()
{
return keySeq;
}
public void setKeySeq(int keySeq)
{
this.keySeq = keySeq;
}
public ImportedKeyRule getUpdateRule()
{
return updateRule;
}
public void setUpdateRule(ImportedKeyRule updateRule)
{
this.updateRule = updateRule;
}
public ImportedKeyRule getDeleteRule()
{
return deleteRule;
}
public void setDeleteRule(ImportedKeyRule deleteRule)
{
this.deleteRule = deleteRule;
}
public String getFkName()
{
return fkName;
}
public void setFkName(String fkName)
{
this.fkName = fkName;
}
public String getPkName()
{
return pkName;
}
public void setPkName(String pkName)
{
this.pkName = pkName;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [pkColumnName=" + pkColumnName + ", fkTableName=" + fkTableName
+ ", fkColumnName=" + fkColumnName + ", keySeq=" + keySeq + ", updateRule=" + updateRule
+ ", deleteRule=" + deleteRule + ", fkName=" + fkName + ", pkName=" + pkName + "]";
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
/**
* {@linkplain ExportedKeyInfo}结果集规范
*
* @author datagear@163.com
*
*/
public class ExportedKeyInfoResultSetSpec extends ResultSetSpec<ExportedKeyInfo>
{
public static final RsColumnSpec<?, ?>[] RS_COLUMN_SPECS = new RsColumnSpec[] {
new RsColumnSpec<String, String>("PKCOLUMN_NAME", String.class, true, false, "pkColumnName"),
new RsColumnSpec<String, String>("FKTABLE_NAME", String.class, true, false, "fkTableName"),
new RsColumnSpec<String, String>("FKCOLUMN_NAME", String.class, true, false, "fkColumnName"),
new RsColumnSpec<Integer, Integer>("KEY_SEQ", Integer.class, false, true, 0, "keySeq"),
new RsColumnSpec<Integer, ImportedKeyRule>("UPDATE_RULE", Integer.class, false, true, "updateRule",
ImportedKeyInfoResultSetSpec.IMPORTED_KEY_RULE_CONVERTER),
new RsColumnSpec<Integer, ImportedKeyRule>("DELETE_RULE", Integer.class, false, true, "deleteRule",
ImportedKeyInfoResultSetSpec.IMPORTED_KEY_RULE_CONVERTER),
new RsColumnSpec<String, String>("FK_NAME", String.class, false, true, "", "fkName"),
new RsColumnSpec<String, String>("PK_NAME", String.class, false, true, "", "pkName") };
public ExportedKeyInfoResultSetSpec()
{
super();
}
@Override
protected Class<ExportedKeyInfo> getRowType()
{
return ExportedKeyInfo.class;
}
@Override
protected RsColumnSpec<?, ?>[] getRsColumnSpecs()
{
return RS_COLUMN_SPECS;
}
}

View File

@ -0,0 +1,180 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.Connection;
import java.util.List;
import org.datagear.connection.ConnectionOption;
/**
* 通用数据库信息解析器
* <p>
* 它从其包含的{@linkplain DevotedDatabaseInfoResolver}
* {@linkplain #getDevotedDatabaseInfoResolvers()}越靠前越优先使用查找能够处理给定{@link Connection}
* 的那一个并使用其API
* </p>
* <p>
* 如果没有查找到能处理给定{@link Connection}{@linkplain DevotedDatabaseInfoResolver}此类将抛出
* {@linkplain UnsupportedDatabaseInfoResolverException}异常
* </p>
*
* @author datagear@163.com
*
*/
public class GenericDatabaseInfoResolver implements DatabaseInfoResolver
{
private List<DevotedDatabaseInfoResolver> devotedDatabaseInfoResolvers = null;
public GenericDatabaseInfoResolver()
{
super();
}
public GenericDatabaseInfoResolver(List<DevotedDatabaseInfoResolver> devotedDatabaseInfoResolvers)
{
super();
this.devotedDatabaseInfoResolvers = devotedDatabaseInfoResolvers;
}
public List<DevotedDatabaseInfoResolver> getDevotedDatabaseInfoResolvers()
{
return devotedDatabaseInfoResolvers;
}
public void setDevotedDatabaseInfoResolvers(List<DevotedDatabaseInfoResolver> devotedDatabaseInfoResolvers)
{
this.devotedDatabaseInfoResolvers = devotedDatabaseInfoResolvers;
}
@Override
public DatabaseInfo getDatabaseInfo(Connection cn) throws DatabaseInfoResolverException
{
DatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolverNotNull(cn);
return databaseInfoResolver.getDatabaseInfo(cn);
}
@Override
public TableInfo[] getTableInfos(Connection cn) throws DatabaseInfoResolverException
{
DatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolverNotNull(cn);
return databaseInfoResolver.getTableInfos(cn);
}
@Override
public TableInfo getRandomTableInfo(Connection cn) throws DatabaseInfoResolverException
{
DatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolverNotNull(cn);
return databaseInfoResolver.getRandomTableInfo(cn);
}
@Override
public TableInfo getTableInfo(Connection cn, String tableName) throws DatabaseInfoResolverException
{
DatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolverNotNull(cn);
return databaseInfoResolver.getTableInfo(cn, tableName);
}
@Override
public EntireTableInfo getEntireTableInfo(Connection cn, String tableName) throws DatabaseInfoResolverException
{
DatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolverNotNull(cn);
return databaseInfoResolver.getEntireTableInfo(cn, tableName);
}
@Override
public ColumnInfo[] getColumnInfos(Connection cn, String tableName) throws DatabaseInfoResolverException
{
DatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolverNotNull(cn);
return databaseInfoResolver.getColumnInfos(cn, tableName);
}
@Override
public ColumnInfo getRandomColumnInfo(Connection cn, String tableName) throws DatabaseInfoResolverException
{
DatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolverNotNull(cn);
return databaseInfoResolver.getRandomColumnInfo(cn, tableName);
}
@Override
public String[] getPrimaryKeyColumnNames(Connection cn, String tableName) throws DatabaseInfoResolverException
{
DatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolverNotNull(cn);
return databaseInfoResolver.getPrimaryKeyColumnNames(cn, tableName);
}
@Override
public String[][] getUniqueKeyColumnNames(Connection cn, String tableName) throws DatabaseInfoResolverException
{
DatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolverNotNull(cn);
return databaseInfoResolver.getUniqueKeyColumnNames(cn, tableName);
}
@Override
public ImportedKeyInfo[] getImportedKeyInfos(Connection cn, String tableName) throws DatabaseInfoResolverException
{
DatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolverNotNull(cn);
return databaseInfoResolver.getImportedKeyInfos(cn, tableName);
}
@Override
public ExportedKeyInfo[] getExportedKeyInfos(Connection cn, String tableName) throws DatabaseInfoResolverException
{
DatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolverNotNull(cn);
return databaseInfoResolver.getExportedKeyInfos(cn, tableName);
}
/**
* 获取支持指定{@code url}{@linkplain DatabaseInfoResolver}
*
* @param cn
* @return
* @throws UnsupportedDatabaseInfoResolverException
*/
protected DevotedDatabaseInfoResolver doGetDevotedDatabaseInfoResolverNotNull(Connection cn)
throws UnsupportedDatabaseInfoResolverException
{
DevotedDatabaseInfoResolver databaseInfoResolver = doGetDevotedDatabaseInfoResolver(cn);
if (databaseInfoResolver == null)
throw new UnsupportedDatabaseInfoResolverException(ConnectionOption.valueOf(cn));
return databaseInfoResolver;
}
/**
* 获取支持指定{@code cn}{@linkplain DevotedDatabaseInfoResolver}
* <p>
* 如果没有则返回{@code null}
* </p>
*
* @param cn
* @return
*/
protected DevotedDatabaseInfoResolver doGetDevotedDatabaseInfoResolver(Connection cn)
{
if (this.devotedDatabaseInfoResolvers == null)
return null;
for (DevotedDatabaseInfoResolver devotedDatabaseInfoResolver : this.devotedDatabaseInfoResolvers)
{
if (devotedDatabaseInfoResolver.supports(cn))
return devotedDatabaseInfoResolver;
}
return null;
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.DatabaseMetaData;
/**
* 表的导入键列信息
* <p>
* 类结构参考{@linkplain DatabaseMetaData#getImportedKeys(String, String, String)}
* 返回结果
* </p>
*
* @author datagear@163.com
*
*/
public class ImportedKeyInfo extends ResultSetSpecBean
{
private static final long serialVersionUID = 1L;
private String pkTableName;
private String pkColumnName;
private String fkColumnName;
private int keySeq;
private ImportedKeyRule updateRule;
private ImportedKeyRule deleteRule;
private String fkName;
private String pkName;
public ImportedKeyInfo()
{
super();
}
public String getPkTableName()
{
return pkTableName;
}
public void setPkTableName(String pkTableName)
{
this.pkTableName = pkTableName;
}
public String getPkColumnName()
{
return pkColumnName;
}
public void setPkColumnName(String pkColumnName)
{
this.pkColumnName = pkColumnName;
}
public String getFkColumnName()
{
return fkColumnName;
}
public void setFkColumnName(String fkColumnName)
{
this.fkColumnName = fkColumnName;
}
public int getKeySeq()
{
return keySeq;
}
public void setKeySeq(int keySeq)
{
this.keySeq = keySeq;
}
public ImportedKeyRule getUpdateRule()
{
return updateRule;
}
public void setUpdateRule(ImportedKeyRule updateRule)
{
this.updateRule = updateRule;
}
public ImportedKeyRule getDeleteRule()
{
return deleteRule;
}
public void setDeleteRule(ImportedKeyRule deleteRule)
{
this.deleteRule = deleteRule;
}
public String getFkName()
{
return fkName;
}
public void setFkName(String fkName)
{
this.fkName = fkName;
}
public String getPkName()
{
return pkName;
}
public void setPkName(String pkName)
{
this.pkName = pkName;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [pkTableName=" + pkTableName + ", pkColumnName=" + pkColumnName
+ ", fkColumnName=" + fkColumnName + ", keySeq=" + keySeq + ", updateRule=" + updateRule
+ ", deleteRule=" + deleteRule + ", fkName=" + fkName + ", pkName=" + pkName + "]";
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.DatabaseMetaData;
/**
* {@linkplain ImportedKeyInfo}结果集规范
*
* @author datagear@163.com
*
*/
public class ImportedKeyInfoResultSetSpec extends ResultSetSpec<ImportedKeyInfo>
{
public static final Converter<Integer, ImportedKeyRule> IMPORTED_KEY_RULE_CONVERTER = new Converter<Integer, ImportedKeyRule>()
{
@Override
public ImportedKeyRule convert(Integer s) throws ResultSetIncompatibleException
{
if (s == null)
return null;
if (DatabaseMetaData.importedKeyNoAction == s)
return ImportedKeyRule.NO_ACTION;
else if (DatabaseMetaData.importedKeyRestrict == s)
return ImportedKeyRule.RESTRICT;
else if (DatabaseMetaData.importedKeyCascade == s)
return ImportedKeyRule.CASCADE;
else if (DatabaseMetaData.importedKeySetNull == s)
return ImportedKeyRule.SET_NULL;
else if (DatabaseMetaData.importedKeySetDefault == s)
return ImportedKeyRule.SET_DEFAUL;
else
return null;
}
};
public static final RsColumnSpec<?, ?>[] RS_COLUMN_SPECS = new RsColumnSpec[] {
new RsColumnSpec<String, String>("PKTABLE_NAME", String.class, true, false, "pkTableName"),
new RsColumnSpec<String, String>("PKCOLUMN_NAME", String.class, true, false, "pkColumnName"),
new RsColumnSpec<String, String>("FKCOLUMN_NAME", String.class, true, false, "fkColumnName"),
new RsColumnSpec<Integer, Integer>("KEY_SEQ", Integer.class, false, true, 0, "keySeq"),
new RsColumnSpec<Integer, ImportedKeyRule>("UPDATE_RULE", Integer.class, false, true, "updateRule",
IMPORTED_KEY_RULE_CONVERTER),
new RsColumnSpec<Integer, ImportedKeyRule>("DELETE_RULE", Integer.class, false, true, "deleteRule",
IMPORTED_KEY_RULE_CONVERTER),
new RsColumnSpec<String, String>("FK_NAME", String.class, false, true, "", "fkName"),
new RsColumnSpec<String, String>("PK_NAME", String.class, false, true, "", "pkName") };
public ImportedKeyInfoResultSetSpec()
{
super();
}
@Override
protected Class<ImportedKeyInfo> getRowType()
{
return ImportedKeyInfo.class;
}
@Override
protected RsColumnSpec<?, ?>[] getRsColumnSpecs()
{
return RS_COLUMN_SPECS;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.DatabaseMetaData;
/**
* 导入键规则
* <p>
* 类结构参考{@linkplain DatabaseMetaData#getImportedKeys(String, String, String)}
* 返回结果
* </p>
*
* @author datagear@163.com
*
*/
public enum ImportedKeyRule
{
NO_ACTION,
CASCADE,
SET_NULL,
SET_DEFAUL,
RESTRICT
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
/**
* 主键列信息
*
* @author datagear@163.com
*
*/
public class PrimaryKeyInfo extends ResultSetSpecBean
{
private static final long serialVersionUID = 1L;
/** 主键列名称 */
private String columnName;
public PrimaryKeyInfo()
{
super();
}
public PrimaryKeyInfo(String columnName)
{
super();
this.columnName = columnName;
}
public String getColumnName()
{
return columnName;
}
public void setColumnName(String columnName)
{
this.columnName = columnName;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [columnName=" + columnName + "]";
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
/**
* {@linkplain PrimaryKeyInfo}结果集规范
*
* @author datagear@163.com
*
*/
public class PrimaryKeyInfoResultSetSpec extends ResultSetSpec<PrimaryKeyInfo>
{
public static final RsColumnSpec<?, ?>[] RS_COLUMN_SPECS = new RsColumnSpec[] {
new RsColumnSpec<String, String>("COLUMN_NAME", String.class, true, false, "columnName") };
public PrimaryKeyInfoResultSetSpec()
{
super();
}
@Override
protected Class<PrimaryKeyInfo> getRowType()
{
return PrimaryKeyInfo.class;
}
@Override
protected RsColumnSpec<?, ?>[] getRsColumnSpecs()
{
return RS_COLUMN_SPECS;
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.ResultSet;
/**
* {@linkplain ResultSet}结果集列缺失异常
*
* @author datagear@163.com
*
*/
public class ResultSetColumnNotPresentException extends ResultSetIncompatibleException
{
private static final long serialVersionUID = 1L;
/** 缺失列名 */
private String columnName;
public ResultSetColumnNotPresentException(String columnName)
{
super("Column [" + columnName + "] is not present");
this.columnName = columnName;
}
public String getColumnName()
{
return columnName;
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
/**
* 结果集不兼容异常
*
* @author datagear@163.com
*
*/
public class ResultSetIncompatibleException extends DatabaseInfoResolverException
{
private static final long serialVersionUID = 1L;
public ResultSetIncompatibleException()
{
super();
}
public ResultSetIncompatibleException(String message)
{
super(message);
}
public ResultSetIncompatibleException(Throwable cause)
{
super(cause);
}
public ResultSetIncompatibleException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -0,0 +1,451 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.datagear.connection.JdbcUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
/**
* 抽象{@linkplain ResultSet}规范
*
* @author datagear@163.com
*
* @param <T>
*/
public abstract class ResultSetSpec<T extends ResultSetSpecBean>
{
private static final Logger log = LoggerFactory.getLogger(ResultSetSpec.class);
public ResultSetSpec()
{
super();
}
/**
* 读取列表
*
* @param rs
* @return
* @throws ResultSetIncompatibleException
* @throws SQLException
*/
public List<T> read(ResultSet rs) throws ResultSetIncompatibleException, SQLException
{
return read(rs, 0, -1);
}
/**
* 读取列表
*
* @param rs
* @param startRow
* 起始行号{@code 1}开始计数
* @param count
* 读取记录数如果{@code <0}表示读取全部
* @return
* @throws ResultSetIncompatibleException
* @throws SQLException
*/
public List<T> read(ResultSet rs, long startRow, int count) throws ResultSetIncompatibleException, SQLException
{
Class<T> rowType = getRowType();
RsColumnSpec<?, ?>[] rsColumnSpecs = getRsColumnSpecs();
return doRead(rs, rsColumnSpecs, rowType, startRow, count);
}
/**
* 获取行目标类型
*
* @return
*/
protected abstract Class<T> getRowType();
/**
* 获取{@linkplain RsColumnSpec}数组
*
* @return
*/
protected abstract RsColumnSpec<?, ?>[] getRsColumnSpecs();
/**
* 读取列表
*
* @param rs
* @param rsColumnSpecs
* @param type
* @param startRow
* 起始行号{@code 1}开始计数
* @param count
* 读取记录数如果{@code <0}表示读取全部
* @return
* @throws ResultSetIncompatibleException
* @throws SQLException
*/
protected <TT> List<TT> doRead(ResultSet rs, RsColumnSpec<?, ?>[] rsColumnSpecs, Class<TT> type, long startRow,
int count) throws ResultSetIncompatibleException, SQLException
{
if (startRow < 1)
startRow = 1;
List<TT> list = new ArrayList<TT>();
ResultSetMetaData rsm = rs.getMetaData();
int columnCount = rsm.getColumnCount();
boolean[] presents = new boolean[rsColumnSpecs.length];
int[] columnIndexes = new int[rsColumnSpecs.length];
for (int i = 1; i <= columnCount; i++)
{
for (int j = 0; j < rsColumnSpecs.length; j++)
{
RsColumnSpec<?, ?> rsColumnSpec = rsColumnSpecs[j];
if (rsm.getColumnLabel(i).equalsIgnoreCase(rsColumnSpec.getName()))
{
presents[j] = true;
columnIndexes[j] = i;
}
}
}
long row = 1;
int readCount = 0;
while (rs.next())
{
if (count >= 0 && readCount >= count)
break;
if (row >= startRow)
{
TT bean = createInstance(type);
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean);
try
{
for (int i = 0; i < rsColumnSpecs.length; i++)
{
@SuppressWarnings("unchecked")
RsColumnSpec<Object, Object> rsColumnSpec = (RsColumnSpec<Object, Object>) rsColumnSpecs[i];
Object propValue = getPropertyValue(rs, row, rsColumnSpec, presents[i], columnIndexes[i]);
beanWrapper.setPropertyValue(rsColumnSpec.getPropertyName(), propValue);
}
list.add(bean);
}
// 如果违反规范则忽略此行数据避免整个功能无法使用
catch (ResultSetValueNullException e)
{
if (log.isWarnEnabled())
log.warn("Reading [" + type.getName() + "] ignores a row data [" + bean + "] ", e);
}
readCount++;
}
row++;
}
return list;
}
/**
* 创建实例
*
* @param clazz
* @return
*/
protected <TT> TT createInstance(Class<TT> clazz)
{
try
{
return clazz.newInstance();
}
catch (Exception e)
{
throw new ResultSetIncompatibleException(e);
}
}
@SuppressWarnings("unchecked")
protected <PT> PT getPropertyValue(ResultSet rs, long row, RsColumnSpec<?, PT> rsColumnSpec, boolean columnPresents,
int columnIndex) throws ResultSetValueNullException, SQLException
{
Object columnValue = null;
if (columnPresents)
{
columnValue = getColumnValue(rs, row, columnIndex, rsColumnSpec.getType());
if (columnValue == null && !rsColumnSpec.isNullable())
throw new ResultSetValueNullException(rsColumnSpec.getName());
}
if (columnValue == null)
columnValue = rsColumnSpec.getDefaultValue();
if (rsColumnSpec.hasConverter())
{
Converter<Object, PT> converter = (Converter<Object, PT>) rsColumnSpec.getConverter();
return converter.convert(columnValue);
}
else
return (PT) columnValue;
}
/**
* {@linkplain ResultSet}中读取{@linkplain String}
*
* @param rs
* @param columnName
* @return
* @throws DatabaseInfoResolverException
*/
protected String getString(ResultSet rs, String columnName) throws DatabaseInfoResolverException
{
try
{
return rs.getString(columnName);
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
}
/**
* {@linkplain ResultSet}中读取{@code int}
*
* @param rs
* @param columnName
* @return
* @throws DatabaseInfoResolverException
*/
protected int getInt(ResultSet rs, String columnName) throws DatabaseInfoResolverException
{
try
{
return rs.getInt(columnName);
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
}
/**
* 获取指定类型的列值
*
* @param rs
* @param row
* @param columnIndex
* @param targetType
* @return
* @throws DatabaseInfoResolverException
*/
protected Object getColumnValue(ResultSet rs, long row, int columnIndex, Class<?> targetType)
throws DatabaseInfoResolverException
{
try
{
return JdbcUtil.getColumnValue(rs, row, columnIndex, targetType);
}
catch (SQLException e)
{
throw new DatabaseInfoResolverException(e);
}
}
/**
* 结果集列规范
*
* @author datagear@163.com
*
* @param <CT>
* @param <PT>
*/
public static class RsColumnSpec<CT, PT> implements Serializable
{
private static final long serialVersionUID = 1L;
/** 列名称 */
private String name;
/** 列类型 */
private Class<CT> type;
/** 是否必须出现 */
private boolean required;
/** 是否允许为null */
private boolean nullable;
/** 当列未出现或者列值为null时的默认列值 */
private CT defaultValue;
/** 对应的属性名称 */
private String propertyName;
/** 列值到属性值的转换器 */
private Converter<CT, PT> converter;
public RsColumnSpec()
{
super();
}
public RsColumnSpec(String name, Class<CT> type, boolean required, boolean nullable, String propertyName)
{
super();
this.name = name;
this.type = type;
this.required = required;
this.nullable = nullable;
this.propertyName = propertyName;
}
public RsColumnSpec(String name, Class<CT> type, boolean required, boolean nullable, String propertyName,
Converter<CT, PT> converter)
{
super();
this.name = name;
this.type = type;
this.required = required;
this.nullable = nullable;
this.propertyName = propertyName;
this.converter = converter;
}
public RsColumnSpec(String name, Class<CT> type, boolean required, boolean nullable, CT defaultValue,
String propertyName)
{
super();
this.name = name;
this.type = type;
this.required = required;
this.nullable = nullable;
this.propertyName = propertyName;
this.defaultValue = defaultValue;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Class<CT> getType()
{
return type;
}
public void setType(Class<CT> type)
{
this.type = type;
}
public boolean isRequired()
{
return required;
}
public void setRequired(boolean required)
{
this.required = required;
}
public boolean isNullable()
{
return nullable;
}
public void setNullable(boolean nullable)
{
this.nullable = nullable;
}
public CT getDefaultValue()
{
return defaultValue;
}
public void setDefaultValue(CT defaultValue)
{
this.defaultValue = defaultValue;
}
public String getPropertyName()
{
return propertyName;
}
public void setPropertyName(String propertyName)
{
this.propertyName = propertyName;
}
public boolean hasConverter()
{
return (this.converter != null);
}
public Converter<CT, PT> getConverter()
{
return converter;
}
public void setConverter(Converter<CT, PT> converter)
{
this.converter = converter;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", type=" + type + ", required=" + required
+ ", nullable=" + nullable + ", defaultValue=" + defaultValue + ", propertyName=" + propertyName
+ ", converter=" + converter + "]";
}
}
/**
* 类型转换器
*
* @author datagear@163.com
*
* @param <S>
* @param <T>
*/
public static interface Converter<S, T>
{
/**
* 将源对象转换为目标对象
*
* @param s
* @return
* @throws ResultSetIncompatibleException
*/
T convert(S s) throws ResultSetIncompatibleException;
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.io.Serializable;
/**
* 结果集规范所对应的Bean
*
* @author datagear@163.com
*
*/
public abstract class ResultSetSpecBean implements Serializable
{
private static final long serialVersionUID = 1L;
public ResultSetSpecBean()
{
super();
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.ResultSet;
/**
* {@linkplain ResultSet}结果集列值为{@code null}异常
*
* @author datagear@163.com
*
*/
public class ResultSetValueNullException extends ResultSetIncompatibleException
{
private static final long serialVersionUID = 1L;
private String columnName;
public ResultSetValueNullException(String columnName)
{
super("Column [" + columnName + "] 's value must not be null");
this.columnName = columnName;
}
public String getColumnName()
{
return columnName;
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.DatabaseMetaData;
/**
* 表信息
* <p>
* 类结构参考
* {@linkplain DatabaseMetaData#getTables(String, String, String, String[])}
* 返回结果
* </p>
*
* @author datagear@163.com
*
*/
public class TableInfo extends ResultSetSpecBean
{
private static final long serialVersionUID = 1L;
/** 名称 */
private String name;
/** 类型 */
private TableType type;
/** 注释 */
private String comment;
public TableInfo()
{
super();
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public TableType getType()
{
return type;
}
public void setType(TableType type)
{
this.type = type;
}
public String getComment()
{
return comment;
}
public void setComment(String comment)
{
this.comment = comment;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", type=" + type + ", comment=" + comment + "]";
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
/**
* {@linkplain TableInfo}结果集规范
*
* @author datagear@163.com
*
*/
public class TableInfoResultSetSpec extends ResultSetSpec<TableInfo>
{
public static final String TABLE_TYPE_TABLE = "TABLE";
public static final String TABLE_TYPE_VIEW = "VIEW";
public static final String TABLE_TYPE_ALIAS = "ALIAS";
public static final Converter<String, TableType> TABLE_TYPE_CONVERTER = new Converter<String, TableType>()
{
@Override
public TableType convert(String type) throws ResultSetIncompatibleException
{
if (TABLE_TYPE_TABLE.equals(type))
return TableType.TABLE;
else if (TABLE_TYPE_VIEW.equals(type))
return TableType.VIEW;
else if (TABLE_TYPE_ALIAS.equals(type))
return TableType.ALIAS;
else
return TableType.TABLE;
}
};
public static final RsColumnSpec<?, ?>[] RS_COLUMN_SPECS = new RsColumnSpec<?, ?>[] {
new RsColumnSpec<String, String>("TABLE_NAME", String.class, true, false, "name"),
new RsColumnSpec<String, TableType>("TABLE_TYPE", String.class, false, true, "type", TABLE_TYPE_CONVERTER),
new RsColumnSpec<String, TableType>("REMARKS", String.class, false, true, "", "comment") };
public TableInfoResultSetSpec()
{
super();
}
@Override
protected Class<TableInfo> getRowType()
{
return TableInfo.class;
}
@Override
protected RsColumnSpec<?, ?>[] getRsColumnSpecs()
{
return RS_COLUMN_SPECS;
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
/**
* 表不存在异常
*
* @author datagear@163.com
*
*/
public class TableNotExistsException extends DatabaseInfoResolverException
{
private static final long serialVersionUID = 1L;
private String tableName;
public TableNotExistsException()
{
super();
}
public TableNotExistsException(String tableName, String message)
{
super(message);
this.tableName = tableName;
}
public TableNotExistsException(String tableName, Throwable cause)
{
super(cause);
this.tableName = tableName;
}
public TableNotExistsException(String tableName, String message, Throwable cause)
{
super(message, cause);
this.tableName = tableName;
}
public String getTableName()
{
return tableName;
}
public void setTableName(String tableName)
{
this.tableName = tableName;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.DatabaseMetaData;
/**
* 表类型
* <p>
* 类结构参考
* {@linkplain DatabaseMetaData#getTables(String, String, String, String[])}
* 返回结果
* </p>
*
* @author datagear@163.com
*
*/
public enum TableType
{
/** 实体表 */
TABLE,
/** 视图 */
VIEW,
/** 别名 */
ALIAS
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
/**
* 唯一键列信息
*
* @author datagear@163.com
*
*/
public class UniqueKeyInfo extends ResultSetSpecBean
{
private static final long serialVersionUID = 1L;
/** 唯一键名称 */
private String keyName;
/** 唯一键列名称 */
private String columnName;
public UniqueKeyInfo()
{
super();
}
public UniqueKeyInfo(String keyName, String columnName)
{
super();
this.keyName = keyName;
this.columnName = columnName;
}
public String getKeyName()
{
return keyName;
}
public void setKeyName(String keyName)
{
this.keyName = keyName;
}
public String getColumnName()
{
return columnName;
}
public void setColumnName(String columnName)
{
this.columnName = columnName;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [keyName=" + keyName + ", columnName=" + columnName + "]";
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
/**
* {@linkplain UniqueKeyInfo}结果集规范
*
* @author datagear@163.com
*
*/
public class UniqueKeyInfoResultSetSpec extends ResultSetSpec<UniqueKeyInfo>
{
public static final RsColumnSpec<?, ?>[] RS_COLUMN_SPECS = new RsColumnSpec[] {
new RsColumnSpec<String, String>("INDEX_NAME", String.class, false, true, "", "keyName"),
new RsColumnSpec<String, String>("COLUMN_NAME", String.class, true, false, "columnName") };
public UniqueKeyInfoResultSetSpec()
{
super();
}
@Override
protected Class<UniqueKeyInfo> getRowType()
{
return UniqueKeyInfo.class;
}
@Override
protected RsColumnSpec<?, ?>[] getRsColumnSpecs()
{
return RS_COLUMN_SPECS;
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import org.datagear.connection.ConnectionOption;
/**
* 不支持数据库信息解析异常
* <p>
* {@linkplain GenericDatabaseInfoResolver}在未找到支持
* {@linkplain DevotedDatabaseInfoResolver}时抛出此异常
* </p>
*
* @author datagear@163.com
*
*/
public class UnsupportedDatabaseInfoResolverException extends DatabaseInfoResolverException
{
private static final long serialVersionUID = 1L;
private ConnectionOption connectionOption;
public UnsupportedDatabaseInfoResolverException(ConnectionOption connectionOption)
{
super("Resolving database info for [" + connectionOption + "] is not supported");
this.connectionOption = connectionOption;
}
public ConnectionOption getConnectionOption()
{
return connectionOption;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.Connection;
/**
* 通配{@linkplain DevotedDatabaseInfoResolver}
* <p>
* 此类的{@linkplain #supports(Connection)}方法始终返回{@linkplain #isSupported()}的值默认为{@code true}
* </p>
* <p>
* 此类可以作为{@linkplain GenericDatabaseInfoResolver#getDevotedDatabaseInfoResolvers()}的最后一个元素用于获取通配数据库信息同时避免它抛出{@linkplain UnsupportedDatabaseInfoResolverException}异常
* </p>
*
* @author datagear@163.com
*
*/
public class WildcardDevotedDatabaseInfoResolver extends AbstractDevotedDatabaseInfoResolver
{
private boolean supported = true;
public WildcardDevotedDatabaseInfoResolver()
{
super();
}
public boolean isSupported()
{
return supported;
}
public void setSupported(boolean supported)
{
this.supported = supported;
}
@Override
public boolean supports(Connection cn)
{
return this.supported;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.Connection;
import java.sql.DriverManager;
/**
* 单元测试支持类
*
* @author datagear@163.com
*
*/
public class DatabaseTestSupport
{
public DatabaseTestSupport()
{
super();
}
protected Connection getMysqlConnection() throws Exception
{
String url = "jdbc:mysql://127.0.0.1:3306/datagear?useUnicode=true&amp;characterEncoding=UTF-8";
return DriverManager.getConnection(url, "root", "");
}
protected void println()
{
System.out.println();
}
protected void println(Object o)
{
System.out.println((o == null ? "null" : o.toString()));
}
protected void print(Object o)
{
System.out.print((o == null ? "null" : o.toString()));
}
}

View File

@ -0,0 +1,236 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbinfo;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import org.datagear.connection.JdbcUtil;
import org.datagear.dbinfo.ColumnInfo;
import org.datagear.dbinfo.DatabaseInfo;
import org.datagear.dbinfo.DevotedDatabaseInfoResolver;
import org.datagear.dbinfo.ExportedKeyInfo;
import org.datagear.dbinfo.GenericDatabaseInfoResolver;
import org.datagear.dbinfo.ImportedKeyInfo;
import org.datagear.dbinfo.TableInfo;
import org.datagear.dbinfo.WildcardDevotedDatabaseInfoResolver;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* {@linkplain MysqlDatabaseInfoResolver}单元测试
*
* @author datagear@163.com
*
*/
public class GenericDatabaseInfoResolverTest extends DatabaseTestSupport
{
private GenericDatabaseInfoResolver genericDatabaseInfoResolver;
public GenericDatabaseInfoResolverTest()
{
super();
List<DevotedDatabaseInfoResolver> devotedDatabaseInfoResolver = new ArrayList<DevotedDatabaseInfoResolver>();
devotedDatabaseInfoResolver.add(new WildcardDevotedDatabaseInfoResolver());
this.genericDatabaseInfoResolver = new GenericDatabaseInfoResolver(devotedDatabaseInfoResolver);
}
@Before
public void setUp() throws Exception
{
}
@After
public void tearDown() throws Exception
{
}
@Test
public void getDatabaseInfoTest() throws Exception
{
Connection cn = getMysqlConnection();
DatabaseInfo databaseInfo = null;
try
{
databaseInfo = this.genericDatabaseInfoResolver.getDatabaseInfo(cn);
}
finally
{
JdbcUtil.closeConnection(cn);
}
// TODO assert
Assert.assertEquals("MySQL", databaseInfo.getProductName());
println(databaseInfo);
}
@Test
public void getTableInfosTest() throws Exception
{
Connection cn = getMysqlConnection();
TableInfo[] tableInfos = null;
try
{
tableInfos = this.genericDatabaseInfoResolver.getTableInfos(cn);
}
finally
{
JdbcUtil.closeConnection(cn);
}
// TODO assert
for (int i = 0; i < tableInfos.length; i++)
{
println(tableInfos[i]);
}
}
@Test
public void getTableInfoTest() throws Exception
{
Connection cn = getMysqlConnection();
TableInfo tableInfo = null;
try
{
tableInfo = this.genericDatabaseInfoResolver.getTableInfo(cn, "T_ORDER");
}
finally
{
JdbcUtil.closeConnection(cn);
}
// TODO assert
println(tableInfo);
}
@Test
public void getColumnInfosTest() throws Exception
{
Connection cn = getMysqlConnection();
ColumnInfo[] columnInfos = null;
try
{
columnInfos = this.genericDatabaseInfoResolver.getColumnInfos(cn, "T_ACCOUNT");
}
finally
{
JdbcUtil.closeConnection(cn);
}
// TODO assert
for (int i = 0; i < columnInfos.length; i++)
{
println(columnInfos[i]);
}
}
@Test
public void getPrimaryKeyColumnNamesTest() throws Exception
{
Connection cn = getMysqlConnection();
String[] primaryKeyColumnNames = null;
try
{
primaryKeyColumnNames = this.genericDatabaseInfoResolver.getPrimaryKeyColumnNames(cn, "T_ORDER");
}
finally
{
JdbcUtil.closeConnection(cn);
}
// TODO assert
Assert.assertArrayEquals(new String[] { "ID" }, primaryKeyColumnNames);
for (int i = 0; i < primaryKeyColumnNames.length; i++)
{
println(primaryKeyColumnNames[i]);
}
}
@Test
public void getImportedKeyInfosTest() throws Exception
{
Connection cn = getMysqlConnection();
ImportedKeyInfo[] importedKeyInfos = null;
try
{
importedKeyInfos = this.genericDatabaseInfoResolver.getImportedKeyInfos(cn, "T_ORDER");
}
finally
{
JdbcUtil.closeConnection(cn);
}
// TODO assert
for (int i = 0; i < importedKeyInfos.length; i++)
{
println(importedKeyInfos[i]);
}
}
@Test
public void getExportedKeyInfosTest() throws Exception
{
Connection cn = getMysqlConnection();
try
{
{
ExportedKeyInfo[] exportedKeyInfos = null;
exportedKeyInfos = this.genericDatabaseInfoResolver.getExportedKeyInfos(cn, "T_ORDER");
// TODO assert
println();
println("T_ORDER");
for (int i = 0; i < exportedKeyInfos.length; i++)
{
println(exportedKeyInfos[i]);
}
}
{
ExportedKeyInfo[] exportedKeyInfos = null;
exportedKeyInfos = this.genericDatabaseInfoResolver.getExportedKeyInfos(cn, "T_ACCOUNT");
// TODO assert
println();
println("T_ACCOUNT");
for (int i = 0; i < exportedKeyInfos.length; i++)
{
println(exportedKeyInfos[i]);
}
}
}
finally
{
JdbcUtil.closeConnection(cn);
}
}
}

49
datagear-dbmodel/pom.xml Normal file
View File

@ -0,0 +1,49 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.datagear</groupId>
<artifactId>datagear</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>datagear-dbmodel</artifactId>
<name>datagear-dbmodel</name>
<dependencies>
<dependency>
<groupId>org.datagear</groupId>
<artifactId>datagear-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.datagear</groupId>
<artifactId>datagear-connection</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.datagear</groupId>
<artifactId>datagear-dbinfo</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.datagear</groupId>
<artifactId>datagear-persistence</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.24</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.sql.Connection;
import org.datagear.connection.ConnectionSensor;
import org.datagear.dbinfo.DatabaseInfoResolver;
/**
* 抽象数据库连接敏感的{@linkplain DevotedDatabaseModelResolver}
* <p>
* 此类可以作为特定数据库{@linkplain DevotedDatabaseModelResolver}实现类的父类
* </p>
*
* @author datagear@163.com
*
*/
public abstract class AbstractConnectionDevotedDatabaseModelResolver extends AbstractDevotedDatabaseModelResolver
{
private ConnectionSensor connectionSensor;
public AbstractConnectionDevotedDatabaseModelResolver()
{
super();
}
public AbstractConnectionDevotedDatabaseModelResolver(ConnectionSensor connectionSensor,
DatabaseInfoResolver databaseInfoResolver, PrimitiveModelResolver primitiveModelResolver)
{
super(databaseInfoResolver, primitiveModelResolver);
this.connectionSensor = connectionSensor;
}
public ConnectionSensor getConnectionSensor()
{
return connectionSensor;
}
public void setConnectionSensor(ConnectionSensor connectionSensor)
{
this.connectionSensor = connectionSensor;
}
@Override
public boolean supports(Connection cn)
{
return this.connectionSensor.supports(cn);
}
}

View File

@ -0,0 +1,143 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.sql.Connection;
import java.util.Collection;
import org.datagear.model.Model;
import org.datagear.model.ModelManager;
import org.datagear.model.Property;
import org.datagear.model.support.AbstractProperty;
import org.datagear.model.support.DefaultModelManager;
import org.datagear.model.support.MU;
/**
* 抽象{@linkplain DbModelFactory}
*
* @author datagear@163.com
*
*/
public abstract class AbstractDbModelFactory implements DbModelFactory
{
private DatabaseModelResolver databaseModelResolver;
private ModelNameResolver modelNameResolver = new SimpleModelNameResolver();
public AbstractDbModelFactory()
{
super();
}
public AbstractDbModelFactory(DatabaseModelResolver databaseModelResolver)
{
super();
this.databaseModelResolver = databaseModelResolver;
}
public DatabaseModelResolver getDatabaseModelResolver()
{
return databaseModelResolver;
}
public void setDatabaseModelResolver(DatabaseModelResolver databaseModelResolver)
{
this.databaseModelResolver = databaseModelResolver;
}
public ModelNameResolver getModelNameResolver()
{
return modelNameResolver;
}
public void setModelNameResolver(ModelNameResolver modelNameResolver)
{
this.modelNameResolver = modelNameResolver;
}
/**
* 解析模型名称
*
* @param tableName
* @return
*/
protected String resolveModelName(String tableName)
{
return this.modelNameResolver.resolve(tableName);
}
/**
* 载入模型并加入全局{@linkplain ModelManager}
*
* @param cn
* @param schema
* @param tableName
* @param globalModelManager
* @return
* @throws DatabaseModelResolverException
*/
protected Model loadModelAndAdd(Connection cn, String schema, String tableName, ModelManager globalModelManager)
throws DatabaseModelResolverException
{
ModelManager localModelManager = new DefaultModelManager();
Model model = this.databaseModelResolver.resolve(cn, globalModelManager, localModelManager, tableName);
// 将globalModelManager中引用的旧Model替换为此新Model
replaceAndAddModel(globalModelManager, model);
Collection<Model> localModels = localModelManager.toCollection();
for (Model localModel : localModels)
{
// 不替换已经存在的Model
if (!globalModelManager.contains(localModel.getName()))
globalModelManager.put(localModel);
}
return model;
}
/**
* 替换新{@linkplain Model}
*
* @param modelManager
* @param model
*/
protected void replaceAndAddModel(ModelManager modelManager, Model model)
{
String modelName = model.getName();
Collection<Model> zyModels = modelManager.toCollection();
for (Model zyModel : zyModels)
{
if (MU.isPrimitiveModel(zyModel))
continue;
Property[] properties = zyModel.getProperties();
for (Property property : properties)
{
Model[] pmodels = property.getModels();
for (int i = 0; i < pmodels.length; i++)
{
if (modelName.equals(pmodels[i].getName()))
pmodels[i] = model;
}
// 如果不是内部数组则需要调用set
if (property.getModels() != pmodels)
{
if (property instanceof AbstractProperty)
((AbstractProperty) property).setModels(pmodels);
else
throw new UnsupportedOperationException("Property.setModels(Model[]) is required for updating");
}
}
}
modelManager.put(model);
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import org.datagear.model.Model;
/**
* 支持缓存的{@linkplain DbModelFactory}
*
* @author datagear@163.com
*
*/
public interface CachedDbModelFactory extends DbModelFactory
{
/**
* 获取缓存中的{@linkplain Model}
* <p>
* 如果缓存中没有将返回{@code null}
* </p>
*
* @param schema
* @param tableName
* @return
*/
Model getCachedModel(String schema, String tableName);
/**
* 移除缓存中的指定{@code schema}{@linkplain Model}
*
* @param schema
*/
void removeCachedModel(String schema);
/**
* 移除缓存中的指定{@code schema}{@linkplain Model}
*
* @param schema
* @param tableName
*/
void removeCachedModel(String schema, String tableName);
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.sql.Connection;
import org.datagear.dbinfo.ColumnInfo;
import org.datagear.dbinfo.EntireTableInfo;
import org.datagear.persistence.features.ColumnConverter;
/**
* {@linkplain ColumnConverter}解析器
*
* @author datagear@163.com
*
*/
public interface ColumnConverterResolver
{
/**
* 解析指定{@linkplain ColumnInfo}对应的基本{@linkplain ColumnConverter}
* <p>
* 返回{@code null}表示不需要
* </p>
*
* @param cn
* @param entireTableInfo
* @param columnInfo
* @return
*/
ColumnConverter resolve(Connection cn, EntireTableInfo entireTableInfo, ColumnInfo columnInfo);
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.sql.Connection;
import org.datagear.model.Model;
import org.datagear.model.ModelManager;
/**
* 数据库{@link Model}解析器
*
* @author datagear@163.com
*
*/
public interface DatabaseModelResolver
{
/**
* 解析指定表的{@link Model}
*
* @param cn
* 数据库连接
* @param globalModelManager
* 全局{@linkplain ModelManager}仅用于查找已被解析的关联表{@link Model}
* @param localModelManager
* 局部{@linkplain ModelManager}用于存储本次解析新产生的关联表{@link Model}
* 此方法的返回结果{@link Model}也将存储于此
* @param table
* 待解析表名称
* @return
* @throws DatabaseModelResolverException
*/
Model resolve(Connection cn, ModelManager globalModelManager, ModelManager localModelManager, String table)
throws DatabaseModelResolverException;
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
/**
* {@link DatabaseModelResolver}异常
*
* @author datagear@163.com
*
*/
public class DatabaseModelResolverException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public DatabaseModelResolverException()
{
super();
}
public DatabaseModelResolverException(String message)
{
super(message);
}
public DatabaseModelResolverException(Throwable cause)
{
super(cause);
}
public DatabaseModelResolverException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.util.Map;
import org.datagear.model.Model;
import org.datagear.model.support.DefaultPrimitiveModelSource;
import org.datagear.model.support.PrimitiveModelSource;
/**
* 数据库{@linkplain PrimitiveModelSource}
*
* @author datagear@163.com
*
*/
public class DatabasePrimitiveModelSource extends DefaultPrimitiveModelSource
{
public DatabasePrimitiveModelSource()
{
super();
}
public DatabasePrimitiveModelSource(Map<Class<?>, Model> primitiveModels)
{
super(primitiveModels);
}
@Override
protected void initDefaultPrimitiveModels()
{
// XXX 目前还没研究是否要添加java.sql.Array, java.sql.Ref, java.sql.RowId,
// java.sql.Struct暂是保留此类
super.initDefaultPrimitiveModels();
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.sql.Connection;
import org.datagear.model.Model;
/**
* 数据库{@linkplain Model}工厂
*
* @author datagear@163.com
*
*/
public interface DbModelFactory
{
/**
* 获取指定数据库表的{@linkplain Model}
*
* @param cn
* @param schema
* @param tableName
* @return
* @throws DbModelFactoryException
*/
Model getModel(Connection cn, String schema, String tableName) throws DbModelFactoryException;
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
/**
* {@linkplain DbModelFactory}异常
*
* @author datagear@163.com
*
*/
public class DbModelFactoryException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public DbModelFactoryException()
{
super();
}
public DbModelFactoryException(String message)
{
super(message);
}
public DbModelFactoryException(Throwable cause)
{
super(cause);
}
public DbModelFactoryException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import org.datagear.connection.ConnectionSensor;
/**
* 专职{@linkplain DatabaseModelResolver}
* <p>
* 此类继承自{@linkplain DatabaseModelResolver}的所有方法仅在{@linkplain #supports(java.sql.Connection)}返回{@code true}时可用
* </p>
*
* @author datagear@163.com
*
*/
public interface DevotedDatabaseModelResolver extends DatabaseModelResolver, ConnectionSensor
{
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.sql.Connection;
import java.util.List;
import org.datagear.connection.ConnectionOption;
import org.datagear.model.Model;
import org.datagear.model.ModelManager;
/**
* 通用数据库{@linkplain Model}解析器
* <p>
* 它从其包含的{@linkplain DevotedDatabaseModelResolver}
* {@linkplain #getDatabaseModelResolvers()}越靠前越优先使用查找能够处理给定{@link Connection}
* 的那一个并使用其API
* </p>
* <p>
* 如果没有查找到能处理给定{@link Connection}{@linkplain DevotedDatabaseModelResolver}
* 此类将抛出 {@linkplain UnsupportedDatabaseModelResolverException}异常
* </p>
*
* @author datagear@163.com
*
*/
public class GenericDatabaseModelResolver implements DatabaseModelResolver
{
private List<DevotedDatabaseModelResolver> devotedDatabaseModelResolvers = null;
public GenericDatabaseModelResolver()
{
super();
}
public GenericDatabaseModelResolver(List<DevotedDatabaseModelResolver> devotedDatabaseModelResolvers)
{
super();
this.devotedDatabaseModelResolvers = devotedDatabaseModelResolvers;
}
public List<DevotedDatabaseModelResolver> getDevotedDatabaseModelResolvers()
{
return devotedDatabaseModelResolvers;
}
public void setDevotedDatabaseModelResolvers(List<DevotedDatabaseModelResolver> devotedDatabaseModelResolvers)
{
this.devotedDatabaseModelResolvers = devotedDatabaseModelResolvers;
}
@Override
public Model resolve(Connection cn, ModelManager globalModelManager, ModelManager localModelManager, String table)
throws DatabaseModelResolverException
{
DatabaseModelResolver databaseModelResolver = doGetDevotedDatabaseModelResolverNotNull(cn);
return databaseModelResolver.resolve(cn, globalModelManager, localModelManager, table);
}
/**
* 获取支持指定{@linkplain Connection}{@linkplain DevotedDatabaseModelResolver}
*
* @param cn
* @return
* @throws UnsupportedDatabaseModelResolverException
*/
protected DevotedDatabaseModelResolver doGetDevotedDatabaseModelResolverNotNull(Connection cn)
throws UnsupportedDatabaseModelResolverException
{
DevotedDatabaseModelResolver devotedDatabaseModelResolver = doGetDevotedDatabaseModelResolver(cn);
if (devotedDatabaseModelResolver == null)
throw new UnsupportedDatabaseModelResolverException(ConnectionOption.valueOf(cn));
return devotedDatabaseModelResolver;
}
/**
* 获取支持指定{@linkplain Connection}{@linkplain DevotedDatabaseModelResolver}
* <p>
* 如果没有则返回{@code null}
* </p>
*
* @param cn
* @return
*/
protected DevotedDatabaseModelResolver doGetDevotedDatabaseModelResolver(Connection cn)
{
if (this.devotedDatabaseModelResolvers == null)
return null;
for (DevotedDatabaseModelResolver devotedDatabaseModelResolver : this.devotedDatabaseModelResolvers)
{
if (devotedDatabaseModelResolver.supports(cn))
return devotedDatabaseModelResolver;
}
return null;
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
/**
* JDBC类型映射支持类
*
* @author datagear@163.com
*
*/
public class JdbcTypeMapSupport
{
public JdbcTypeMapSupport()
{
super();
}
/**
* 将JDBC类型字面映射表转换为JDBC类型值映射表
*
* @param literalMap
* @return
*/
protected <T> Map<Integer, T> literalMapToValueMap(Map<String, T> literalMap)
{
Map<Integer, T> valueMap = new HashMap<Integer, T>();
for (Map.Entry<String, T> entry : literalMap.entrySet())
{
try
{
java.lang.reflect.Field field = Types.class.getField(entry.getKey());
Integer jdbcType = (Integer) field.get(null);
valueMap.put(jdbcType, entry.getValue());
}
catch (NoSuchFieldException e)
{
throw new IllegalArgumentException("Getting field [" + entry.getKey() + "] value of ["
+ Types.class.getName() + "] exception occurs, maybe no such field");
}
catch (IllegalAccessException e)
{
throw new IllegalArgumentException("Getting field [" + entry.getKey() + "] value of ["
+ Types.class.getName() + "] exception occurs, maybe no such field");
}
}
return valueMap;
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import org.datagear.model.Model;
/**
* {@linkplain Model}名称解析器
*
* @author datagear@163.com
*
*/
public interface ModelNameResolver
{
/**
* 解析{@linkplain Model}名称
*
* @param tableName
* 表名称
* @return
*/
String resolve(String tableName);
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.io.Serializable;
import org.datagear.model.Model;
/**
* 占位类型
* <p>
* {@linkplain AbstractDevotedDatabaseModelResolver}使用此类构建占位{@linkplain Model}
* </p>
*
* @author datagear@163.com
*
*/
public interface PlaceHolderType extends Serializable
{
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.sql.Connection;
import org.datagear.dbinfo.ColumnInfo;
import org.datagear.dbinfo.EntireTableInfo;
import org.datagear.model.Model;
/**
* 基本{@linkplain Model}解析器
*
* @author datagear@163.com
*
*/
public interface PrimitiveModelResolver
{
/**
* 解析指定{@linkplain ColumnInfo}对应的基本{@linkplain Model}
* <p>
* 返回{@code null}表示无法解析
* </p>
*
* @param cn
* @param entireTableInfo
* @param columnInfo
* @return
*/
Model resolve(Connection cn, EntireTableInfo entireTableInfo, ColumnInfo columnInfo);
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
/**
* 简单{@linkplain ModelNameResolver}
*
* @author datagear@163.com
*
*/
public class SimpleModelNameResolver implements ModelNameResolver
{
public SimpleModelNameResolver()
{
super();
}
@Override
public String resolve(String tableName)
{
return tableName;
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.sql.Connection;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
import org.datagear.dbinfo.ColumnInfo;
import org.datagear.dbinfo.EntireTableInfo;
import org.datagear.model.Model;
import org.datagear.persistence.columnconverter.BlobColumnConverter;
import org.datagear.persistence.columnconverter.BytesColumnConverter;
import org.datagear.persistence.columnconverter.ClobColumnConverter;
import org.datagear.persistence.columnconverter.StringColumnConverter;
import org.datagear.persistence.features.ColumnConverter;
/**
* 基于类型映射表的{@linkplain ColumnConverterResolver}
* <p>
* 此类的{@linkplain #getJdbcTypeMap()}主键是{@linkplain Types}类的JDBC类型字段值是对应的{@linkplain Model#getType()}
* </p>
* <p>
* 此类会默认添加如下类型映射
* </p>
*
* <pre>
* Types.BINARY -> new BytesColumnConverter()
* Types.BLOB -> new BlobColumnConverter()
* Types.CLOB -> new ClobColumnConverter()
* Types.LONGNVARCHAR -> new StringColumnConverter()
* Types.LONGVARBINARY -> new BytesColumnConverter()
* Types.LONGVARCHAR -> new StringColumnConverter()
* Types.NCLOB -> new ClobColumnConverter()
* Types.VARBINARY -> new BytesColumnConverter()
* </pre>
*
* @author datagear@163.com
*
*/
public class TypeMapColumnConverterResolver extends JdbcTypeMapSupport implements ColumnConverterResolver
{
private Map<Integer, ColumnConverter> columnConverterMap = new HashMap<Integer, ColumnConverter>();
public TypeMapColumnConverterResolver()
{
super();
initDefaultColumnConverterMap();
}
/**
* 初始化默认类型映射表
*/
public void initDefaultColumnConverterMap()
{
this.columnConverterMap.put(Types.BINARY, new BytesColumnConverter());
this.columnConverterMap.put(Types.BLOB, new BlobColumnConverter());
this.columnConverterMap.put(Types.CLOB, new ClobColumnConverter());
this.columnConverterMap.put(Types.LONGNVARCHAR, new StringColumnConverter());
this.columnConverterMap.put(Types.LONGVARBINARY, new BytesColumnConverter());
this.columnConverterMap.put(Types.LONGVARCHAR, new StringColumnConverter());
this.columnConverterMap.put(Types.NCLOB, new ClobColumnConverter());
this.columnConverterMap.put(Types.VARBINARY, new BytesColumnConverter());
}
public Map<Integer, ColumnConverter> getColumnConverterMap()
{
return columnConverterMap;
}
public void setColumnConverterMap(Map<Integer, ColumnConverter> columnConverterMap)
{
this.columnConverterMap = columnConverterMap;
}
/**
* 设置字面型映射表
* <p>
* 它的主键是{@linkplain Types}类的JDBC类型字段名此方法会自动将它们转换为对应的字段值
* </p>
* <p>
* 此方法可以在XML配置中使用使其更友好
* </p>
*
* @param literalColumnConverterMap
*/
public void setLiteralColumnConverterMap(Map<String, ColumnConverter> literalColumnConverterMap)
{
Map<Integer, ColumnConverter> jdbcTypeMap = literalMapToValueMap(literalColumnConverterMap);
this.columnConverterMap.putAll(jdbcTypeMap);
}
@Override
public ColumnConverter resolve(Connection cn, EntireTableInfo entireTableInfo, ColumnInfo columnInfo)
{
int jdbcType = columnInfo.getType();
ColumnConverter columnConverter = this.columnConverterMap.get(jdbcType);
return columnConverter;
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.sql.Connection;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
import org.datagear.dbinfo.ColumnInfo;
import org.datagear.dbinfo.EntireTableInfo;
import org.datagear.model.Model;
import org.datagear.model.support.PrimitiveModelSource;
/**
* 基于类型映射表的{@linkplain PrimitiveModelResolver}
* <p>
* 此类的{@linkplain #getModelTypeMap()}主键是{@linkplain Types}类的JDBC类型字段值是对应的{@linkplain Model#getType()}
* </p>
* <p>
* 此类会默认添加如下类型映射
* </p>
*
* <pre>
* Types.BIGINT -> Long.class
* Types.BINARY -> java.io.File.class
* Types.BIT -> Boolean.class
* Types.BLOB -> java.io.File.class
* Types.BOOLEAN -> Boolean.class
* Types.CHAR -> String.class
* Types.CLOB -> String.class
* Types.DATE -> java.sql.Date.class
* Types.DECIMAL -> java.math.BigDecimal.class
* Types.DOUBLE -> Double.class
* Types.FLOAT -> Float.class
* Types.INTEGER -> Integer.class
* Types.LONGNVARCHAR -> String.class
* Types.LONGVARBINARY -> java.io.File.class
* Types.LONGVARCHAR -> String.class
* Types.NCHAR -> String.class
* Types.NCLOB -> String.class
* Types.NUMERIC -> java.math.BigDecimal.class
* Types.NVARCHAR -> String.class
* Types.REAL -> Float.class
* Types.SMALLINT -> Short.class
* Types.TIME -> java.sql.Time.class
* Types.TIMESTAMP -> java.sql.Timestamp.class
* Types.TINYINT -> Byte.class
* Types.VARBINARY -> java.io.File.class
* Types.VARCHAR -> String.class
* </pre>
*
* @author datagear@163.com
*
*/
public class TypeMapPrimitiveModelResolver extends JdbcTypeMapSupport implements PrimitiveModelResolver
{
private PrimitiveModelSource primitiveModelSource;
private Map<Integer, Class<?>> modelTypeMap = new HashMap<Integer, Class<?>>();
public TypeMapPrimitiveModelResolver()
{
super();
initDefaultModelTypeMap();
}
public TypeMapPrimitiveModelResolver(PrimitiveModelSource primitiveModelSource)
{
super();
this.primitiveModelSource = primitiveModelSource;
initDefaultModelTypeMap();
}
/**
* 初始化默认JDBC类型值映射表
*/
protected void initDefaultModelTypeMap()
{
this.modelTypeMap.put(Types.BIGINT, Long.class);
this.modelTypeMap.put(Types.BINARY, java.io.File.class);
this.modelTypeMap.put(Types.BIT, Boolean.class);
this.modelTypeMap.put(Types.BLOB, java.io.File.class);
this.modelTypeMap.put(Types.BOOLEAN, Boolean.class);
this.modelTypeMap.put(Types.CHAR, String.class);
this.modelTypeMap.put(Types.CLOB, String.class);
this.modelTypeMap.put(Types.DATE, java.sql.Date.class);
this.modelTypeMap.put(Types.DECIMAL, java.math.BigDecimal.class);
this.modelTypeMap.put(Types.DOUBLE, Double.class);
this.modelTypeMap.put(Types.FLOAT, Float.class);
this.modelTypeMap.put(Types.INTEGER, Integer.class);
this.modelTypeMap.put(Types.LONGNVARCHAR, String.class);
this.modelTypeMap.put(Types.LONGVARBINARY, java.io.File.class);
this.modelTypeMap.put(Types.LONGVARCHAR, String.class);
this.modelTypeMap.put(Types.NCHAR, String.class);
this.modelTypeMap.put(Types.NCLOB, String.class);
this.modelTypeMap.put(Types.NUMERIC, java.math.BigDecimal.class);
this.modelTypeMap.put(Types.NVARCHAR, String.class);
this.modelTypeMap.put(Types.REAL, Float.class);
this.modelTypeMap.put(Types.SMALLINT, Short.class);
this.modelTypeMap.put(Types.TIME, java.sql.Time.class);
this.modelTypeMap.put(Types.TIMESTAMP, java.sql.Timestamp.class);
this.modelTypeMap.put(Types.TINYINT, Byte.class);
this.modelTypeMap.put(Types.VARBINARY, java.io.File.class);
this.modelTypeMap.put(Types.VARCHAR, String.class);
}
public PrimitiveModelSource getPrimitiveModelSource()
{
return primitiveModelSource;
}
public void setPrimitiveModelSource(PrimitiveModelSource primitiveModelSource)
{
this.primitiveModelSource = primitiveModelSource;
}
public Map<Integer, Class<?>> getModelTypeMap()
{
return modelTypeMap;
}
public void setModelTypeMap(Map<Integer, Class<?>> modelTypeMap)
{
this.modelTypeMap = modelTypeMap;
}
/**
* 设置字面类型映射表
* <p>
* 它的主键是{@linkplain Types}类的JDBC类型字段名此方法会自动将它们转换为对应的字段值
* </p>
* <p>
* 此方法可以在XML配置中使用使其更友好
* </p>
*
* @param literalModelTypeMap
*/
public void setLiteralModelTypeMap(Map<String, Class<?>> literalModelTypeMap)
{
Map<Integer, Class<?>> jdbcTypeMap = literalMapToValueMap(literalModelTypeMap);
this.modelTypeMap.putAll(jdbcTypeMap);
}
@Override
public Model resolve(Connection cn, EntireTableInfo entireTableInfo, ColumnInfo columnInfo)
{
int jdbcType = columnInfo.getType();
Class<?> modelType = this.modelTypeMap.get(jdbcType);
Model model = (modelType == null ? null : this.primitiveModelSource.get(modelType));
return model;
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import org.datagear.dbinfo.ColumnInfo;
import org.datagear.model.Model;
/**
* 不支持解析列模型异常
*
* @author datagear@163.com
*
*/
public class UnsupportedColumnModelResolvingException extends DatabaseModelResolverException
{
private static final long serialVersionUID = 1L;
private ColumnInfo columnInfo;
public UnsupportedColumnModelResolvingException()
{
super();
}
public UnsupportedColumnModelResolvingException(ColumnInfo columnInfo)
{
super("Resolving [" + columnInfo + "] 's [" + Model.class.getSimpleName() + "] is not supported");
this.columnInfo = columnInfo;
}
public ColumnInfo getColumnInfo()
{
return columnInfo;
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import org.datagear.connection.ConnectionOption;
import org.datagear.model.Model;
/**
* 不支持数据库模型解析异常
* <p>
* {@linkplain GenericDatabaseModelResolver}在未找到支持
* {@linkplain DevotedDatabaseModelResolver}时抛出此异常
* </p>
*
* @author datagear@163.com
*
*/
public class UnsupportedDatabaseModelResolverException extends DatabaseModelResolverException
{
private static final long serialVersionUID = 1L;
private ConnectionOption connectionOption;
public UnsupportedDatabaseModelResolverException(ConnectionOption connectionOption)
{
super("Resolving database " + Model.class.getSimpleName() + " for [" + connectionOption + "] is not supported");
this.connectionOption = connectionOption;
}
public ConnectionOption getConnectionOption()
{
return connectionOption;
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
/**
* 不支持的JDBC数据类型异常
*
* @author datagear@163.com
*
*/
public class UnsupportedJdbcTypeException extends DatabaseModelResolverException
{
private static final long serialVersionUID = 1L;
private int jdbcType;
public UnsupportedJdbcTypeException()
{
super();
}
public UnsupportedJdbcTypeException(int jdbcType, String message)
{
super(message);
this.jdbcType = jdbcType;
}
public UnsupportedJdbcTypeException(int jdbcType, Throwable cause)
{
super(cause);
this.jdbcType = jdbcType;
}
public UnsupportedJdbcTypeException(int jdbcType, String message, Throwable cause)
{
super(message, cause);
this.jdbcType = jdbcType;
}
public int getJdbcType()
{
return jdbcType;
}
public void setJdbcType(int jdbcType)
{
this.jdbcType = jdbcType;
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.sql.Connection;
import org.datagear.dbinfo.DatabaseInfoResolver;
/**
* 通配{@linkplain DevotedDatabaseModelResolver}
* <p>
* 此类的{@linkplain #supports(Connection)}方法始终返回{@linkplain #isSupported()}的值默认为{@code true}
* </p>
* <p>
* 此类可以作为{@linkplain GenericDatabaseModelResolver#getDevotedDatabaseModelResolvers()}的最后一个元素用于解析通配模型信息同时避免它抛出{@linkplain UnsupportedDatabaseModelResolverException}异常
* </p>
*
* @author datagear@163.com
*
*/
public class WildcardDevotedDatabaseModelResolver extends AbstractDevotedDatabaseModelResolver
{
private boolean supported = true;
public WildcardDevotedDatabaseModelResolver()
{
super();
}
public WildcardDevotedDatabaseModelResolver(DatabaseInfoResolver databaseInfoResolver,
PrimitiveModelResolver primitiveModelResolver)
{
super(databaseInfoResolver, primitiveModelResolver);
}
public boolean isSupported()
{
return supported;
}
public void setSupported(boolean supported)
{
this.supported = supported;
}
@Override
public boolean supports(Connection cn)
{
return this.supported;
}
}

View File

@ -0,0 +1,302 @@
/*
* Copyright (c) 2018 by datagear.org.
*/
package org.datagear.dbmodel;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Properties;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* {@linkplain DatabaseMetaData}测试
*
* @author datagear@163.com
*
*/
public class DatabaseMetaDataTest extends TestSupport
{
@Before
public void setUp() throws Exception
{
}
@After
public void tearDown() throws Exception
{
}
@Test
public void getIdentifierQuoteStringTest() throws Exception
{
Connection cn = getMysqlConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
println("getIdentifierQuoteStringTest : ");
println(metaData.getIdentifierQuoteString());
println("");
}
finally
{
cn.close();
}
}
@Test
public void getCatalogsTest() throws Exception
{
Connection cn = getMysqlConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getCatalogs();
printlnResultSet(rs, "getCatalogs");
}
finally
{
cn.close();
}
}
@Test
public void getSchemasTest() throws Exception
{
Connection cn = getMysqlConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getSchemas();
printlnResultSet(rs, "getSchemas");
}
finally
{
cn.close();
}
}
@Test
public void getUserNameTest() throws Exception
{
Connection cn = getMysqlConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
String username = metaData.getUserName();
println("getUserNameTest : ");
println(username);
println("");
}
finally
{
cn.close();
}
}
@Test
public void getTableTypesTest() throws Exception
{
Connection cn = getMysqlConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getTableTypes();
printlnResultSet(rs, "getTableTypes");
}
finally
{
cn.close();
}
}
@Test
public void getTablesTest() throws Exception
{
Properties props = new Properties();
props.setProperty("useInformationSchema", "true");
Connection cn = getMysqlConnection(props);
try
{
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getTables(cn.getCatalog(), null, "%", new String[] { "TABLE" });
printlnResultSet(rs, "getTables");
}
finally
{
cn.close();
}
}
@Test
public void getPrimaryKeysTest() throws Exception
{
Connection cn = getMysqlConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getPrimaryKeys(cn.getCatalog(), null, "T_ACCOUNT");
printlnResultSet(rs, "getPrimaryKeys");
}
finally
{
cn.close();
}
}
@Test
public void getColumnTest() throws Exception
{
Connection cn = getMysqlConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getColumns(cn.getCatalog(), null, "%", "%");
printlnResultSet(rs, "getColumns");
}
finally
{
cn.close();
}
}
@Test
public void getExportedKeysTest() throws Exception
{
Connection cn = getMysqlConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getExportedKeys(cn.getCatalog(), null, "T_ACCOUNT");
printlnResultSet(rs, "getExportedKeys");
}
finally
{
cn.close();
}
}
@Test
public void getImportedKeysTest() throws Exception
{
Connection cn = getMysqlConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getImportedKeys(cn.getCatalog(), null, "T_ORDER");
printlnResultSet(rs, "getImportedKeys");
}
finally
{
cn.close();
}
}
@Test
public void getUniqueKeysTest() throws Exception
{
Connection cn = getMysqlConnection();
try
{
DatabaseMetaData metaData = cn.getMetaData();
ResultSet rs = metaData.getIndexInfo(cn.getCatalog(), null, "T_ADDRESS", true, false);
printlnResultSet(rs, "getUniqueKeys");
}
finally
{
cn.close();
}
}
/**
* 打印{@linkplain ResultSet}
*
* @param rs
* @param label
* @throws SQLException
*/
protected void printlnResultSet(ResultSet rs, String label) throws SQLException
{
ResultSetMetaData rsMeta = rs.getMetaData();
int colCount = rsMeta.getColumnCount();
println("---" + label + "--------------------------------------------");
for (int i = 1; i <= colCount; i++)
{
print(rsMeta.getColumnLabel(i) + "(" + rsMeta.getColumnType(i) + ", " + rsMeta.getColumnTypeName(i) + ", "
+ rsMeta.getColumnClassName(i) + "), ");
}
println("");
for (int i = 1; i <= colCount; i++)
{
print(rsMeta.getColumnLabel(i) + ", ");
}
println("");
while (rs.next())
{
for (int i = 1; i <= colCount; i++)
{
print(rs.getObject(i) + ", ");
}
println("");
}
println("---" + label + "--------------------------------------------");
println("");
}
protected Connection getMysqlConnection(Properties properties) throws SQLException
{
properties.setProperty("user", "root");
properties.setProperty("password", "");
return DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/datagear?useUnicode=true&amp;characterEncoding=UTF-8", properties);
}
}

Some files were not shown because too many files have changed in this diff Show More