Files
emo-grab/src/main/java/quant/rich/emoney/config/SqliteMybatisConfig.java
2025-11-16 04:26:20 +08:00

129 lines
5.4 KiB
Java

package quant.rich.emoney.config;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import quant.rich.emoney.EmoneyAutoApplication;
@Slf4j
@Configuration
@MapperScan(basePackages = "quant.rich.emoney.mapper.sqlite", sqlSessionTemplateRef = "sqliteSqlSessionTemplate")
public class SqliteMybatisConfig {
private static final String RESOURCE_PATH = "database.db";
public static final String SQLITE_TRANSACTION_MANAGER = "sqliteTransactionManager";
/**
* 配置数据库连接
* <ul>
* <li>如果非打包状态,则直接选取当前项目内数据库位置</li>
* <li>如果打包状态,以 JDBC 链接位置为主,如果位置不存在则覆盖</li>
* </ul>
* @param dataSource
*/
public void initSQLiteLocation(DataSource dataSource) {
// 指定 sqlite 路径
if (dataSource instanceof HikariDataSource hikariDataSource) {
String filePath = hikariDataSource.getJdbcUrl();
if (filePath == null || !filePath.startsWith("jdbc:sqlite:")) {
log.warn(
"无法在 SQLite HikariDataSource 中找到合法 SQLite JDBC url, "
+ "数据库可能会加载失败。合法的 url 需在 application.yml(properties) "
+ "中配置,以 jdbc:sqlite: 开头。当前获取到的 jdbc-url: {}", filePath);
return;
}
filePath = filePath.substring("jdbc:sqlite:".length()).trim();
ClassPathResource original = new ClassPathResource(RESOURCE_PATH);
if (!original.exists()) {
log.warn("未找到 SQLite 资源: {}", RESOURCE_PATH);
return;
}
String protocol = EmoneyAutoApplication.class.getProtectionDomain().getCodeSource().getLocation().getProtocol();
boolean isJar = "jar".equals(protocol);
if (isJar) {
// 复制到外部 yml 指定路径,已存在则不复制
File dest = new File(filePath), parentDir = dest.getParentFile();
String destAbsolutePath = dest.getAbsolutePath();
if (!parentDir.exists() && !parentDir.mkdirs()) {
log.warn("无法创建放置 SQLite 文件的目录: {}", parentDir.getAbsolutePath());
return;
}
if (dest.exists()) {
// 已存在
log.warn("目标资源 {} 已存在,忽略", destAbsolutePath);
return;
}
try (InputStream in = getClass().getClassLoader().getResourceAsStream(RESOURCE_PATH)) {
if (in == null) {
log.warn("无法读取 SQLite 资源: {}", RESOURCE_PATH);
return;
}
Files.copy(in, dest.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
log.info("SQLite 数据库文件已复制至:{}", destAbsolutePath);
} catch (Exception e) {
log.warn("复制 SQLite 数据库文件失败", e);
}
}
else {
// 使用当前绝对路径
Path path = Path.of("src/main/resources", RESOURCE_PATH);
hikariDataSource.setJdbcUrl("jdbc:sqlite:" + path.toAbsolutePath().toString());
}
}
}
@Bean("sqliteSqlSessionFactory")
public SqlSessionFactory sqliteSqlSessionFactory(
@Qualifier("sqliteDataSource") DataSource dataSource) throws Exception {
initSQLiteLocation(dataSource);
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(dataSource);
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.SQLITE));
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
factory.setPlugins(interceptor);
return factory.getObject();
}
@Bean("sqliteSqlSessionTemplate")
public SqlSessionTemplate sqliteSqlSessionTemplate(@Qualifier("sqliteSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean(SQLITE_TRANSACTION_MANAGER)
public DataSourceTransactionManager postgreTransacetionManager(@Qualifier("sqliteDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}