文章目录
最开始,java通过JDBC访问数据库,JDBC的缺点
- jdbc没有使用连接池,操作数据库需要频繁的创建、关闭连接,浪费数据库资源;
- sql语句写在java代码中,如果sql语句修改,java代码需要重新编译,不利于系统维护;
- 通过prepareStatement向占位符设置参数,存在硬编码问题,占位符和参数需要一一对应;
- 解析结果集时,存在硬编码,且需要对结果集进行遍历。
现在可以用Mybatis替换JDBC连接数据库。
MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录,用于实现面向对象编程语言里不同类型系统间的数据转换
两种方式的优缺点:
注解不适合比较复杂的sql语句,如关联查询等;而且没有对sql语句进行统一的管理;修改需要重新编译;
xml方式增加了xml文件;对条件不确定的查询语句需要if判断;有特殊的转义字符,如小于;
一、代码
1.1 启动方法
public class MybatisTest {
public static void main(String[] args) {
String resources = "MybatisConfig.xml";
try {
Reader resourceAsReader = Resources.getResourceAsReader(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.selectOne("org.apache.ibatis.UserDao.getUser");
// 另一种方法
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.selectUser("1");
} catch (IOException e) {
e.printStackTrace();
}
}
}
如果要使用sqlSession.getMapper(UserDao.class)的方法执行sql语句,必须要在配置文件里用package和class的方式加载映射文件。
1.2 配置文件MybatisConfig.xml
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://10.2.73.183:3306/user_server"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="user/UserMapper.xml"/>
</mappers>
</configuration>
1.2.1 配置文件的结构
类型别名typeAliases:为 Java 类型设置一个短的名字,存在的意义仅在于用来减少类完全限定名的冗余。
类型处理器typeHandlers:无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型
映射器mappers:定义映射文件所在的位置。有xml和注解两种方式实现映射器,
1.3 UserMapper.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="org.apache.ibatis.UserDao">
<select id="getUser" resultType="org.apache.ibatis.User">
select id, username, password from sys_user
</select>
</mapper>
1.4 UserDao
public interface UserDao {
User getUser();
}
二、本质
Mybatis连接数据库必备的几个组件:
- DML:insert、delete、update语句
- DQL:查询语句
- DDL:建表语句
2.1 Mybatis如何获取数据源
用户调用new SqlSessionFactoryBuilder().build(stream);
,build()方法中调用XMLConfigBuilder.parse()解析mybatis的配置文件中<configuration>标签下的配置,build()、parse()方法如下:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
parseConfiguration()方法如下,root中存的就是MybatisConfig.xml中<configuration>标签下的内容。
- **environmentsElement()**用来解析关于数据源的配置;
- propertiesElement(root.evalNode(“properties”));:解析的是mybatis配置文件中在properties标签下定义的变量,因为最先解析的是properties,所以在配置文件中properties标签要在最上面;
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
propertiesElement()方法如下,首先获取配置文件中properties标签下的配置,再去获取指定路径文件中的配置,如果有相同的name,文件中的会覆盖标签中的,最后将配置放入configuration和parser的variables属性中
,优先级:url指定文件中的配置>resources指定文件中的配置>property标签中的配置。
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
Properties defaults = context.getChildrenAsProperties();
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
environmentsElement()方法如下:
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
mybatis内置提供JDBC和MANAGED两种事务管理方式,前者主要用于简单JDBC模式,后者主要用于容器管理事务,一般使用JDBC事务管理方式。
dataSourceElement()方法用于解析dataSource标签下的内容,得到数据源工厂,通过DataSourceFactory.getDataSource()获取到数据源对象后,通过构建者模式得到Enviroment对象,并将Enviroment对象放入Configuration(全局java配置)对象中。
dataSourceElement()方法如下:
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
// 根据数据源类型获取对应类对象, 本文中type="POOLED",所以得到的对象为PooledDataSourceFactory
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
factory.setProperties(props);