如果是Hibernate,反向生成数据库大家肯定会想到hbm2ddl,但是ORMLite呢?ORMLite虽然提供了按照对象来反向生成表的方法,但还不够。因为我希望.新增一个表,不要额外单独写创建表、删除表的操作。
因此想到了Spring的packagesToScan,不过暂未深究Spring,只好找了个现成的类。实现非子目录的扫描:
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
public class ReflectionUtil {
private ReflectionUtil() {}
public static List<Class<? extends Annotation>> getClassesWithAnnotation(String packageName, Class<? extends Annotation> annotationType) throws ClassNotFoundException, IOException{
List<Class<? extends Annotation>> annotatedClassList = new ArrayList<Class<? extends Annotation>>();
for(Class type: getClasses(packageName)){
if(type.getAnnotation(annotationType) != null){
annotatedClassList.add(type);
}
}
return annotatedClassList;
}
/**
* Scans all classes accessible from the context class loader which belong
* to the given package and subpackages.
*
* @param packageName
* The base package
* @return The classes
* @throws ClassNotFoundException
* @throws IOException
*/
public static List<Class<?>> getClasses(String packageName) throws ClassNotFoundException, IOException
{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
List<File> dirs = new ArrayList<File>();
while (resources.hasMoreElements())
{
URL resource = resources.nextElement();
dirs.add(new File(resource.getFile()));
}
List<Class<?>> classes = new ArrayList<Class<?>>();
for (File directory : dirs)
{
classes.addAll(findClasses(directory, packageName));
}
return classes;
}
/**
* Recursive method used to find all classes in a given directory and
* subdirs.
*
* @param directory
* The base directory
* @param packageName
* The package name for classes found inside the base directory
* @return The classes
* @throws ClassNotFoundException
*/
private static List<Class<?>> findClasses(File directory, String packageName) throws ClassNotFoundException
{
List<Class<?>> classes = new ArrayList<Class<?>>();
if (!directory.exists())
{
return classes;
}
File[] files = directory.listFiles();
for (File file : files)
{
if (file.isDirectory())
{
classes.addAll(findClasses(file, packageName + "." + file.getName()));
}
else if (file.getName().endsWith(".class"))
{
classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
}
}
return classes;
}
}
完成后就可以在Spring的InitializingBean去干初始化系统的活了,调用ORMLite的数据库操作功能,先drop,后create:
for(Class<? extends Annotation> type: ReflectionUtil.getClassesWithAnnotation("org.我的包.com", DatabaseTable.class)){
TableUtils.dropTable(connectionSource, type, true);
TableUtils.createTable(connectionSource, type);
}
这里提醒下,大家Spring一般用的是DataSource,而ORMLite用的是ConnectionSource,在ConnectionSource构造方法里有包装DataSource的方法,在spring里配置下即可:
<bean id="connectionSource" class="com.j256.ormlite.jdbc.DataSourceConnectionSource" init-method="initialize">
<property name="dataSource" ref="dataSource" />
<property name="databaseUrl" value="${dataSource.driverUrl}" />
</bean>
不知道如何采用spring package-scan机制完成,看看spring源码理论上应该是可行的。暂未深究,在此就算抛砖引玉了。
/* -------------------- 2016年的分割线 -------------------------- */
之前看过包扫描的代码,自己居然给忘了,这记性……
用包扫描来代替ReflectionUtil,写起来其实非常简单:
for(Class<? extends Annotation> type: ReflectionUtil.getClassesWithAnnotation("org.我的包.com", DatabaseTable.class)){
可以直接换成:
for(Class<? extends Annotation> type: scanOrmLiteClasses("classpath:org/我的包/com/*/xxxx/*.class")){
还支持部分路径匹配,方便极了。scanOrmLiteClasses的代码如下:
@SuppressWarnings({ "unchecked", "rawtypes" })
private List<Class<? extends Annotation>> scanOrmLiteClasses(String pattern) throws URISyntaxException, IOException, ClassNotFoundException{
List<Class<? extends Annotation>> annotatedClassList = new ArrayList<Class<? extends Annotation>>();
int pathLen = Thread.currentThread().getContextClassLoader().getResource(".").toURI().toString().length();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
for(Resource resources: resolver.getResources(pattern)){
String uriStr = resources.getURI().toString();
Class scanClass = Class.forName(uriStr.substring(pathLen, uriStr.length() - 6).replaceAll("/", "."));
if(scanClass.getAnnotation(DatabaseTable.class) != null){
annotatedClassList.add(scanClass);
}
}
return annotatedClassList;
}