在实际项目开发中,经常有换数据库的需求,这里将介绍怎样在原数据库基础上扩展一种数据库实现,并实现数据库切换
在此之前,如果你还不太了解抽象工厂设计模式,可以先了解其实现: 抽象工厂模式_行者Beck的博客-CSDN博客
1. 在配置文件中定义数据库模式配置: db.mode=db1
2. 定义抽象工厂,实现不同db的具体工厂,在具体工厂里生成不同db的Dao接口实现 (这里使用context.getBean()是为了防止DaoImpl中使用@Autowired注入的类为null)
/**
* @author beck.yang
* @date 2022/7/11 10:49
* @description 抽象工厂
*/
public interface AbstractDBFactory {
<T extends Dao> T createDao();
}
/**
* @author beck.yang
* @date 2022/7/11 10:51
* @description
*/
public class DB1Factory implements AbstractDBFactory {
private static ApplicationContext context;
@Override
public <T extends Dao> T createDao() {
return (T) context.getBean(DB1DaoImpl.class);
}
}
/**
* @author beck.yang
* @date 2022/7/11 10:51
* @description
*/
public class DB2Factory implements AbstractDBFactory{
private static ApplicationContext context;
@Override
public <T extends Dao> T createDao() {
return (T) context.getBean(DB2DaoImpl.class);
}
}
3. 原有的Dao接口及DB1DaoImpl实现类,我们需要扩展DB2DaoImpl,实现Dao接口
/**
* @author beck.yang
* @date 2022/7/11 10:50
* @description
*/
public interface Dao {
void create();
void delete();
}
/**
* @author beck.yang
* @date 2022/7/11 10:53
* @description
*/
public class DB1DaoImpl implements Dao{
@Override
public void create() {
}
@Override
public void delete() {
}
}
/**
* @author beck.yang
* @date 2022/7/11 10:53
* @description
*/
public class DB2DaoImpl implements Dao{
@Override
public void create() {
}
@Override
public void delete() {
}
}
4. 定义FactoryUtil,获取db.mode配置的数据库模式,这里使用了@Value静态注入
/**
* @author beck.yang
* @date 2022/7/11 10:56
* @description
*/
@Slf4j
public class FactoryUtil {
public static String dbMode = "db1";
@Setter
@Value("${db.mode}")
public void setDbMode(String dbMode) {
com.cloudwise.cmdb.core.factory.FactoryUtil.dbMode = dbMode;
}
public static String getDbMode() {
return dbMode;
}
public static DatabaseFactory getFactory() {
if ("db1".equals(getDbMode())) {
return new ArangoFactory();
} else if ("db2".equals(getDbMode())) {
return new Neo4jFactory();
} else {
throw new RuntimeException("The conf of ${db.mode} isn't support");
}
}
public static Object of(Class<?> clazz) {
try {
DatabaseFactory factory = getFactory();
Method[] methods = factory.getClass().getDeclaredMethods();
for (Method method : methods) {
Class<?> returnType = method.getReturnType();
if (returnType.equals(clazz)) {
method.setAccessible(true);
return method.invoke(factory);
}
}
} catch (Exception e) {
log.error("FactoryUtil build dao error, ", e);
}
return null;
}
}
5. 定义@DaoAutowired注解,实现Dao层注入的封装
/**
* @author beck.yang
* @date 2022/7/7 15:00
* @description
*/
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DaoAutowired {
}
4. 定义@DaoAutowired注解的处理类,这里实现了BeanPostProcessor(实现ApplicationContextAware是为了避免调用具体工厂类时context还未被装载)
/**
* @author beck.yang
* @date 2022/7/7 15:05
* @description
*/
@Slf4j
public class DaoAutowiredProcessor implements ApplicationContextAware, BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object obj, String beanName) throws BeansException {
Field[] declaredFields = obj.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
DaoAutowired dbAutowired = declaredField.getAnnotation(DaoAutowired.class);
if (dbAutowired != null) {
Class<?> type = declaredField.getType();
Object bean = FactoryUtil.of(type);
declaredField.setAccessible(true);
try {
declaredField.set(obj, bean);
} catch (IllegalAccessException e) {
log.error("@DaoAutowired error, ", e);
}
}
}
return obj;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
DB1Factory.context = applicationContext;
DB2Factory.context = applicationContext;
}
}
5. 最后一步,也是不可或缺的一步,一定要在META-INF下创建spring.factories文件并增加注解处理类的配置:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.easytuop.factory.DaoAutowiredProcessor
至此,就可以像使用@Autowired一样使用@DaoAutowired注入Dao了,并且注入时并不关注是具体哪种数据库的实现!!!
@DaoAutowired
private Dao dao;
6. 实现时发现,有更简单的方式可以达到相同目的,那就是使用@ConditionalOnProperty注解,使用该注解可以根据配置文件project.database.model配置的数据库模式装载需要的Dao层实现类,使用时直接 @Autowired Dao dao;
/**
* @author beck.yang
* @date 2022/7/11 10:50
* @description
*/
@Repository
@ConditionalOnProperty(prefix = "project.database", value = "mode", havingValue = "db1")
public class DB1DaoImpl implements Dao{
@Override
public void create() {
}
@Override
public void delete() {
}
}
/**
* @author beck.yang
* @date 2022/7/11 10:53
* @description
*/
@Repository
@ConditionalOnProperty(prefix = "project.database", value = "mode", havingValue = "db2")
public class DB2DaoImpl implements Dao{
@Override
public void create() {
}
@Override
public void delete() {
}
}