forked from p85126437/datagear
添加初始代码
This commit is contained in:
parent
1a673148eb
commit
d15c997891
|
@ -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*
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
abcd
|
Binary file not shown.
Binary file not shown.
|
@ -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
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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.0(JDK1.6)中需要将其设置为null,才符合DatabaseMetaData.getTables(...)等接口的参数要求
|
||||
String schema = null;
|
||||
|
||||
// JDBC4.1(JDK1.7)才有Connection.getSchema()接口,为了兼容性,本应用采用了JDBC4.0(JDK1.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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&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()));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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&characterEncoding=UTF-8", properties);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue