Mybatis 参数化现实多数据源
首先我们知道Mybatis可以利用配置文件实现多数据源的配置,且可以实现数据的切换。
但是我在项目中遇到一个问题是:数据源的地址、端口等一些列信息,甚至表都要需要动态获取,也就是说不能先直接在配置文件配置。
我这里的解决办法是:动态生成XML配置文件的方式配置数据源,并且配置化操作某一个张表。当前操作的表还不多,可以这样做。如果直接操作一个库,那肯定是要用多数据配置文件的方式了。
在项目Mybatis、数据源等依赖引入之后,我们需要多引入下面一个依赖,来实现构建XML文件:
(这里本人主要也是学习一下 XML文件和Java对象之间的转换工具XStream)
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.10</version>
</dependency>
一、正常操作示例
整体目录结构:
1.1 建表语句
CREATE TABLE `person` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(64) DEFAULT NULL,
`sex` char(4) DEFAULT NULL,
KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=127 DEFAULT CHARSET=utf8mb3;
insert into `person`(`id`,`name`,`sex`)
values (101,'王麻子','男'),
(102,'娜美','女'),
(103,'娜美','女'),
(104,'娜美','女'),
(105,'小明','男'),
(107,'小明','男');
1.2 Mapper
@Data
public class Person implements Serializable {
private Integer id;
private String name;
private String sex;
}
@Mapper
public interface PersonMapper {
List<Person> queryList(@Param("sex")String sex);
}
PersonMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yogurt.example.dao.PersonMapper">
<resultMap id="BaseResultMap" type="com.yogurt.example.entity.Person">
<result column="id" jdbcType="INTEGER" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="sex" jdbcType="CHAR" property="sex" />
</resultMap>
<select id="queryList" parameterType="java.lang.String" resultType="com.yogurt.example.entity.Person">
select * from ${tableName}
where sex = #{sex}
</select>
</mapper>
1.3 配置文件
ThirdMybatisConfig.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="url" value="jdbc:mysql://localhost:3306/yogurtlearn?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mybatis/PersonMapper.xml"/>
</mappers>
</configuration>
1.4 查询步骤
public class ThirdMybatisConfigTest {
@Test
public void testMyBatisBuild() throws IOException {
// 1、获取配置文件流;
InputStream input = Resources.getResourceAsStream("mybatis/ThirdMybatisConfig.xml");
// 2、解析XML配置文件,构建 SqlSessionFactory;
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(input);
// 3、利用SqlSessionFactory创建 SqlSession 会话;
SqlSession sqlSession = sessionFactory.openSession();
// 4、利用 SqlSession 创建 Mapper接口 的代理对象 MapperProxy;
PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
// 5、然后在执行 Mapper接口 的 SQL查询时,转而利用代理对象执行 JDBC的SQL底层操作。
List<Person> persons = mapper.queryList("person","男");
}
}
二、数据源参数化改造
我们主要就是改造查询步骤的第一步,获取配置文件流,正常都是通过路径找到的;这里我们直接通过构造xml配置文件生成配置文件流。
利用 XStream工具 让Java对象转xml配置文件;构建配置文件Configuration对应的Java对象。
新增三个文件,实现对象转XML:
2.1 构建Configuration对象
@Data
@XStreamAlias("configuration") // 给XML标签起个别名
public class Configuration {
public Environments environments = new Environments();
public Mappers mappers = new Mappers();;
public Configuration() {
}
@Data
@XStreamAlias("mappers") // mappers标签
public static class Mappers{
@XStreamImplicit // mapper数组
private List<Mapper> mapper = new ArrayList<Mapper>();;
public void addMapper(Mapper mapper) {
this.mapper.add(mapper);
}
@Data
@XStreamAlias("mapper")
public static class Mapper{
@XStreamAlias("resource") // mapper标签上的属性 resource
@XStreamAsAttribute
private String resource;
public Mapper(String resource) {
this.resource = resource;
}
}
}
@Data
@XStreamAlias("environments") // environments 环境标签
public static class Environments {
@XStreamAlias("default") // environments 环境标签上的属性 default
@XStreamAsAttribute
private String environments;
private Environment environment;
public Environments() {
this.environments = "development"; // 环境默认 development
this.environment = new Environment();
}
@Data
@XStreamAlias("environment")
public static class Environment {
@XStreamAlias("id")
@XStreamAsAttribute
private String environment;
private TransactionManager transactionManager;
private DataSource dataSource;
public Environment() {
this.environment = "development";
this.transactionManager = new TransactionManager();
this.dataSource = new DataSource();
}
@Data
@XStreamAlias("transactionManager") // 事务管理器标签
public static class TransactionManager {
@XStreamAlias("type")
@XStreamAsAttribute
private String transactionManager;
public TransactionManager() {
this.transactionManager = "JDBC"; // 默认JDBC类型
}
}
@Data
@XStreamAlias("dataSource") // 数据源标签 dataSource
public static class DataSource {
@XStreamAlias("type")
@XStreamAsAttribute
private String dataSource;
public DataSource() {
}
@XStreamImplicit
private List<Property> property = new ArrayList<Property>();
public DataSource(String driver, String username, String password, String ip, Integer port, String database) {
this.dataSource = "POOLED";
// 这里在构造方法里面默认add了4个 数据源标签属性,其实,我们可以在new对象的时候一个一个添加。
this.property.add(new Property("driver",driver));
this.property.add(new Property("username",username));
this.property.add(new Property("password",password));
this.property.add(new Property("url","jdbc:mysql://" +ip+ ":" +port+ "/" +database+ "?characterEncoding=utf8&characterSetResults=utf8&serverTimezone=GMT%2b8&autoReconnect=true"));
}
}
@Data
@XStreamAlias("property") // property 标签
public static class Property {
@XStreamAlias("name")
@XStreamAsAttribute
private String name; // property标签上属性name
@XStreamAlias("value")
@XStreamAsAttribute
private String value; // property标签上属性value
public Property(String name, String value) {
this.name = name;
this.value = value;
}
}
}
}
}
2.2 处理XML声明
/**
* 增加XML声明,默认XStream是没有XML声明的
*/
public class XmlUtf8XStream extends XStream {
/**
* xml版本号,默认1.0
*/
private String version;
/**
* xml编码,默认UTF-8
*/
private String encoding;
public XmlUtf8XStream() {
this.version = "1.0";
this.encoding = "UTF-8";
}
// XML的声明
// 这里直接是Mybatis配置文件的声明;我们也可以通过传参的方式配置文件声明
public String getDeclaration() {
return "<?xml version=\"" + this.version + "\" encoding=\"" + this.encoding + "\"?>\n"
+ "<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n";
}
/**
* 覆盖父类的方法,然后调用父类的,输出的时候先输出这个XML的声明
* @param obj
* @param output
*/
@Override
public void toXML(Object obj, OutputStream output){
try {
String dec = this.getDeclaration();
byte[] bytesOfDec = dec.getBytes("UTF-8");
//先输出XML的声明
output.write(bytesOfDec);
} catch (Exception e) {
throw new RuntimeException("error", e);
}
// 再调用父类
super.toXML(obj, output);
}
@Override
public void toXML(Object obj, Writer writer) {
try {
//先输出XML的声明
writer.write(getDeclaration());
} catch (Exception e) {
throw new RuntimeException("error", e);
}
// 再调用父类
super.toXML(obj, writer);
}
}
2.3 获取SqlSession
public class XmlConfigUtil {
/**
* 获取当前数据源的 SqlSession
*/
public static SqlSession dataQueryBase(String driver,
String username,String password,
String host,Integer port,String database,
String xmlMapperPath){
// 1、获取配置文件流
InputStream input = xmlConfigInputStream(driver, username, password, host, port, database, xmlMapperPath);
// 2、解析XML配置文件,构建 SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(input);
// 3、利用SqlSessionFactory创建 SqlSession 会话
SqlSession sqlSession = sessionFactory.openSession();
return sqlSession;
}
/**
* 输入数据源信息,构建Configuration对象,生成Mybatis xml配置文件流
*/
private static InputStream xmlConfigInputStream(String driver,
String username,String password,
String host,Integer port,String database,
String xmlMapperPath){
XStream xStream = new XmlUtf8XStream();
xStream.autodetectAnnotations(true);// XStream开启注解方式自动检测
// ★构建 Mybatis xml配置文件
String xmlConfigFile = xStream.toXML(getConfiguration(driver, username, password, host, port, database, xmlMapperPath));
return new ByteArrayInputStream(xmlConfigFile.getBytes());
}
/**
* 构建 Configuration对象
* @param driver com.mysql.jdbc.Driver
* @param xmlMapperPath ege: PersonMapper.xml -> mybatis/mapper/PersonMapper.xml
* @return
*/
private static Configuration getConfiguration(String driver,
String username,String password,
String host,Integer port,String database,
String xmlMapperPath) {
Configuration configuration = new Configuration();
Configuration.Environments.Environment.DataSource dataSource =
new Configuration.Environments.Environment.DataSource(
driver,
username, password,
host, port, database);
configuration.getEnvironments().getEnvironment().setDataSource(dataSource);
Configuration.Mappers.Mapper mapper = new Configuration.Mappers.Mapper(xmlMapperPath);
configuration.getMappers().addMapper(mapper);
return configuration;
}
}
2.4 查询步骤
这里我们就可以参数化配置数据源信息了,然后获取到SqlSession对象,对数据库进行操作。
public class ThirdMybatisConfigTest {
@Test
public void testXmlConfigUtil() throws IOException {
// 1~3步骤在这里完成
SqlSession sqlSession = XmlConfigUtil.dataQueryBase("com.mysql.jdbc.Driver",
"root", "123456",
"127.0.0.1", 3306, "yogurtlean",
"mybatis/mapper/PersonMapper.xml");
// 4、利用 SqlSession 创建 Mapper接口 的代理对象 MapperProxy;
PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
// 5、然后在执行 Mapper接口 的 SQL查询时,转而利用代理对象执行 JDBC的SQL底层操作。
List<Person> persons = mapper.queryList("person","男");
}
}
引用:
- 链接一:blog.51cto.com/u_11554106/… (xml文件 声明DTD 问题)
- 链接二:www.wenjiangs.com/doc/xijhqlo… (XStream教程)