文章目录
-
- MyBatis 入门
- 从 XML 中构建 SqlSessionFactory
- 从 SqlSessionFactory 中获取 SqlSession
- Mybatis补充
- Mybatis接口注解
- Mybatis增删改查(CURD)
- XML 映射配置文件
- MyBatis 加载属性的顺序
- mybatis settings设置
- mybatis类型别名(typeAliases)
- Mybatis 插件(plugins)
- Mybatis配置环境(environments)
- Mybatis事务管理器(transactionManager)
- Mybatis 数据源(dataSource)
- MyBatis 第一种内建数据源类型`UNPOOLED`
- MyBatis 第二种内建数据源类型`POOLED`
- ## MyBatis 第二种内建数据源类型`JNDI`
- MyBatis XML映射文件
- Mybatis select , insert, update 和 delete元素属性的配置
- MyBatis resultMap元素
- Mybatis使用Map存储结果集
- Mybatis使用POJO存储结果集
- resultType和resultMap的区别
- MyBatis注解
- MyBatis关联查询
- MyBatis一对一关联查询
- MyBatis 一对一关联查询处理过程
- MyBatis 一对一关联查询的分步查询处理过程
MyBatis 入门
-
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。
-
MyBatis 消除 了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。
-
MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。
-
要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。
-
如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
从 XML 中构建 SqlSessionFactory
- 每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。
- SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。
- 从 XML 文件中构建 SqlSessionFactory 的实例使用类路径下的资源文件进行配置,也可以使用输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流
- MyBatis有一个实用类----Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。
<?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="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
- XML 头部的声明,它用来验证 XML 文档的正确性。environment 元素体中包含了事务管理和连接池的配置。
- mappers 元素则包含了一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息。
从 SqlSessionFactory 中获取 SqlSession
- 可以从SqlSessionFactory中获得 SqlSession 的实例
- SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
- 可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
try (SqlSession session = sqlSessionFactory.openSession()) {
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}
- 上述方式可以执行,但是较为麻烦,现在有了一种更简洁的方式——使用和指定语句的参数和返回值相匹配的接口(比如 BlogMapper.class),这种方式的优点在于不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(
BlogMapper.class
);
Blog blog = mapper.selectBlog(101);
}
- 对于像 BlogMapper 这样的映射器类来说,可以不用 XML 来配置,而可以使用 Java 注解来配置。比如,上面的 XML 示例可以被替换成如下的配置:
package org.mybatis.example;
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
}
- 使用注解来映射简单语句会是代码显得更加简洁,但是对于复杂的SQL语句最好用 XML 来映射语句。因为使用注解会使得sql语句混乱
Mybatis补充
- 作用域(Scope)和生命周期
- 依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器,并将它们直接注入到你的 bean 中,因此可以直接忽略它们的生命周期
- SqlSessionFactoryBuilder的使用
- SqlSessionFactoryBuilder可以被实例化,使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了
- SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)
- 可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- SqlSession的使用
- 每个线程都应该有它自己的 SqlSession 实例,SqlSession 的实例不是线程安全的,因此是不能被共享的,所以SqlSession 的最佳的作用域是请求或方法作用域
- 不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行
- 不能将 SqlSession 实例的引用放在任何类型的托管作用域中
- 映射器实例的使用
- 映射器是一些绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的
- 映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃
- 映射器实例并不需要被显式地关闭,但是最好将映射器放在方法作用域内
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(
BlogMapper.class
); // 逻辑代码 }
Mybatis接口注解
- 创建一个Java项目,
- 在 src 源码目录下创建一个包:com.yiibai.mybatis.dao,并建立接口类 IUser 及一个方法, 在方法上面,使用SQL注释,内容如下:
package com.yiibai.mybatis.dao;
import org.apache.ibatis.annotations.Select;
import com.yiibai.mybatis.models.User;
/**
* @author yiibai.com
*/
public interface IUser {
@Select("select * from user where id= #{id}")
public User getUserByID(int id);
}
- getUserByID 必须与 User.xml 里面配置的 select 的 id 对应()同名,虽使用注解映射不需要User.xml
- 配置 MyBatis 所需的数据连接文件,这里创建一个文件:
src/config/Configure.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>
<typeAliases>
<typeAlias alias="User" type="com.yiibai.mybatis.models.User" />
</typeAliases>
<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://127.0.0.1:3306/testdb" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<mappers>
<!-- // power by http://www.yiibai.com 注释掉咯...
<mapper resource="com/yiibai/mybatis/models/User.xml" />-->
</mappers>
</configuration>
- 在包:com.yiibai.mybatis.models 下创建一个User.java 类文件
package com.yiibai.mybatis.models;
public class User {
private int id;
private String name;
private String dept;
private String phone;
private String website;
public String getWebsite() {
return website;
}
public void setWebsite(String website) {
this.website = website;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
- 与 User.java 对应的 XML 配置文件:User.xml 可以少省略
- 在 src 这个目录下创建一个类:Main.java,Main.java 的代码详细内容如下
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.yiibai.mybatis.dao.IUser;
import com.yiibai.mybatis.models.User;
public class Main {
private static SqlSessionFactory sqlSessionFactory;
private static Reader reader;
static {
try {
reader = Resources.getResourceAsReader("config/Configure.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
sqlSessionFactory.getConfiguration().addMapper(IUser.class);
} catch (Exception e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSession() {
return sqlSessionFactory;
}
public static void main(String[] args) {
SqlSession session = sqlSessionFactory.openSession();
try {
IUser iuser = session.getMapper(IUser.class);
User user = iuser.getUserByID(1);
System.out.println("名字:"+user.getName());
System.out.println("所属部门:"+user.getDept());
System.out.println("主页:"+user.getWebsite());
} finally {
session.close();
}
}
}
Mybatis增删改查(CURD)
- 创建一个工程:mybatis,加入所需的 jar 包:mysql-connector 和 mybatis3.jar。配置 src/config/Configure.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>
<typeAliases>
<typeAlias alias="User" type="com.yiibai.mybatis.models.User" />
</typeAliases>
<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://127.0.0.1:3306/testdb" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<mappers>
<!-- // power by http://www.yiibai.com -->
<mapper resource="com/yiibai/mybatis/models/User.xml" />
</mappers>
</configuration>
- 创建一个类和一个接口:User.java类和IUser.java接口,User.java类位于包 com.yiibai.mybatis.models 下,User.java类代码内容
package com.yiibai.mybatis.models;
public class User {
private int id;
private String name;
private String dept;
private String phone;
private String website;
public String getWebsite() {
return website;
}
public void setWebsite(String website) {
this.website = website;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
- IUser.java接口位于包com.yiibai.mybatis.dao 下,IUser.java接口代码内容如下
package com.yiibai.mybatis.dao;
import java.util.List;
import org.apache.ibatis.annotations.Select;
import com.yiibai.mybatis.models.User;
public interface IUser {
//@Select("select * from user where id= #{id}")
//public User getUserByID(int id);
public List<User> getUserList();
public void insertUser(User user);
public void updateUser(User user);
public void deleteUser(int userId);
public User getUser(int id);
}
- User.xml对应了增删改查的操作(每一个操作的 ID 对应于IUser接口的方法),
<?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.yiibai.mybatis.dao.IUser">
<select id="getUser" parameterType="int"
resultType="com.yiibai.mybatis.models.User">
SELECT *
FROM USER
WHERE id = #{
userId}
</select>
<insert id="insertUser" parameterType="User">
INSERT INTO USER(name,
dept, website,phone)
VALUES(#{
name}, #{
dept}, #{
website}, #{
phone})
</insert>
<select id="getUserList" resultType="com.yiibai.mybatis.models.User">
SELECT * FROM USER
</select>
<update id="updateUser" parameterType="User">
UPDATE USER
SET
name=
#{name},
dept = #{
dept},
website = #{
website},
phone = #{
phone}
WHERE
id =
#{id}
</update>
<delete id="deleteUser" parameterType="int">
DELETE FROM USER WHERE id = #{
id}
</delete>
</mapper>
- main程序内容
import java.io.Reader;
import java.text.MessageFormat;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.yiibai.mybatis.dao.IUser;
import com.yiibai.mybatis.models.User;
public class Main {
private static SqlSessionFactory sqlSessionFactory;
private static Reader reader;
static {
try {
reader = Resources.getResourceAsReader("config/Configure.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (Exception e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSession() {
return sqlSessionFactory;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
SqlSession session = sqlSessionFactory.openSession();
try {
//sqlSessionFactory.getConfiguration().addMapper(IUser.class);
//User user = (User) session.selectOne( "com.yiibai.mybatis.models.UserMapper.getUserByID", 1);
// 用户数据列表
getUserList();
// 插入数据
// testInsert();
// 更新用户
//testUpdate();
// 删除数据
//testDelete();
} finally {
session.close();
}
}
//
public static void testInsert()
{
try
{
// 获取Session连接
SqlSession session = sqlSessionFactory.openSession();
// 获取Mapper
IUser userMapper = session.getMapper(IUser.class);
System.out.println("Test insert start...");
// 执行插入
User user = new User();
user.setId(0);
user.setName("Google");
user.setDept("Tech");
user.setWebsite("http://www.google.com");
user.setPhone("120");
userMapper.insertUser(user);
// 提交事务
session.commit();
// 显示插入之后User信息
System.out.println("After insert");
getUserList();
System.out.println("Test insert finished...");
}
catch (Exception e)
{
e.printStackTrace();
}
}
// 获取用户列表
public static void getUserList() {
try {
SqlSession session = sqlSessionFactory.openSession();
IUser iuser = session.getMapper(IUser.class);
// 显示User信息
System.out.println("Test Get start...");
printUsers(iuser.getUserList());
System.out.println("Test Get finished...");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void testUpdate()
{
try
{
SqlSession session = sqlSessionFactory.openSession();
IUser iuser = session.getMapper(IUser.class);
System.out.println("Test update start...");
printUsers(iuser.getUserList());
// 执行更新
User user = iuser.getUser(1);
user.setName("New name");
iuser.updateUser(user);
// 提交事务
session.commit();
// 显示更新之后User信息
System.out.println("After update");
printUsers(iuser.getUserList());
System.out.println("Test update finished...");
}catch (Exception e)
{
e.printStackTrace();
}
}
// 删除用户信息
public static void testDelete()
{
try
{
SqlSession session = sqlSessionFactory.openSession();
IUser iuser = session.getMapper(IUser.class);
System.out.println("Test delete start...");
// 显示删除之前User信息
System.out.println("Before delete");
printUsers(iuser.getUserList());
// 执行删除
iuser.deleteUser(2);
// 提交事务
session.commit();
// 显示删除之后User信息
System.out.println("After delete");
printUsers(iuser.getUserList());
System.out.println("Test delete finished...");
}catch (Exception e)
{
e.printStackTrace();
}
}
/**
*
* 打印用户信息到控制台
*
* @param users
*/
private static void printUsers(final List<User> users) {
int count = 0;
for (User user : users) {
System.out.println(MessageFormat.format(
"============= User[{0}]=================", ++count));
System.out.println("User Id: " + user.getId());
System.out.println("User Name: " + user.getName());
System.out.println("User Dept: " + user.getDept());
System.out.println("User Website: " + user.getWebsite());
}
}
}
XML 映射配置文件
- MyBatis 的配置文件包含了影响 MyBatis 行为的设置(settings)和属性(properties)信息。
- 属性(properties)都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,也可通过 properties 元素的子元素来传递。例如:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
- 属性就可以在整个配置文件中使用来替换需要动态配置的属性值
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
- username 和 password 将会由 properties 元素中设置的相应值来替换
- driver 和 url 属性将会由 config.properties 文件中对应的值来替换
- 属性也可以被传递到 SqlSessionBuilder.build()方法中
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, props);
// ... or ...
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, props);
MyBatis 加载属性的顺序
- 如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:
- 第一被加载的是在 properties 元素体内指定的属性
- 第二被加载的是properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
- 第三被加载的是作为方法参数传递的属性,并覆盖已读取的同名属性
- 总之,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性。
mybatis settings设置
- MyBatis 中的设置会改变 MyBatis 的运行时行为
- cacheEnabled,该配置影响的所有映射器中配置的缓存的全局开关。默认值为true
- lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。默认值false
- useColumnLabel使用列标签代替列名。不同的驱动在这方面会有不同的表现,默认值true
- defaultExecutorType配置默认的执行器。
- SIMPLE 就是普通的执行器;
- REUSE 执行器会重用预处理语句(prepared statements);
- BATCH 执行器将重用语句并执行批量更新。
- 默认值为SIMPLE
- mapUnderscoreToCamelCase是否开启自动驼峰命名规则(camel case)映射,
- 即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。
- 默认值为false
一个配置完整的 settings 元素的示例如下:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
mybatis类型别名(typeAliases)
- 类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
- 当这样配置时,Blog可以用在任何使用domain.blog.Blog的地方。也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
- 每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author.若有注解,则别名为其注解值。看下面的例子:
@Alias("author")
public class Author {
...
}
Mybatis 插件(plugins)
- MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。
- MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
- 通过 MyBatis使用插件只需实现 Interceptor 接口,并指定了想要拦截的方法签名
// ExamplePlugin.java
@Intercepts({
@Signature(
type= Executor.class,
method = "update",
args = {
MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
- 上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行低层映射语句的内部对象。
- NOTE 覆盖配置类:除了用插件来修改 MyBatis 核心行为之外,还可以通过完全覆盖配置类来达到目的
- 只需继承后覆盖其中的每个方法,再把它传递到 sqlSessionFactoryBuilder.build(myConfig) 方法即可
Mybatis配置环境(environments)
- MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中,例如,开发、测试和生产环境需要有不同的配置;或者共享相同 Schema 的多个生产数据库, 想使用相同的 SQL 映射。许多类似的用例。
- 尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其中的一个
- 如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,即每个数据库对应一个 SqlSessionFactory 实例
- 为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。可以接受环境配置的两个方法签名是:
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment,properties);
- 如果忽略了环境参数,那么默认环境将会被加载,如下所示
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader,properties);
- 环境元素定义了如何配置环境
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
- 默认的环境 ID(比如:default=“development”)。
- 每个 environment 元素定义的环境 ID(比如:id=“development”)。
- 事务管理器的配置(比如:type=“JDBC”)。
- 数据源的配置(比如:type=“POOLED”)。
- 默认的环境和环境 ID 要保证默认环境要匹配其中一个环境ID。
Mybatis事务管理器(transactionManager)
- 在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- JDBC – 这个配置使用了 JDBC 的提交和回滚设置,依赖于从数据源得到的连接来管理事务范围。
- MANAGED – 从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
- 如果使用 Spring + MyBatis,则没有必要配置事务管理器, 因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
Mybatis 数据源(dataSource)
- dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源
- MyBatis 的应用程序将会按示例中的例子来配置数据源。然而它并不是必须的。要知道为了方便使用延迟加载,数据源才是必须的。
- MyBatis 有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")
MyBatis 第一种内建数据源类型UNPOOLED
1. MyBatis 第一种内建数据源类型`UNPOOLED`– 这个数据源的实现只是每次被请求时打开和关闭连接,速度较慢,但是对在及时可用连接方面没有性能要求的简单应用程序是一个很好的选择。
2. 不同的数据库在这方面表现也是不一样的,所以对某些数据库来说使用连接池并不重要,这个配置也是理想的
- UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:
- driver – 这是 JDBC 驱动的 Java 类的完全限定名(并不是JDBC驱动中可能包含的数据源类)
- url – 这是数据库的 JDBC URL 地址。
- username – 登录数据库的用户名。
- password – 登录数据库的密码。
- defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
6.同时还可以使用 driver.encoding=UTF8,传递属性给数据库驱动。要这样做,属性的前缀为"driver.",这将通过DriverManager.getConnection(url,driverProperties)方法传递值为 UTF8 的 encoding 属性给数据库驱动。
MyBatis 第二种内建数据源类型POOLED
- POOLED– 数据源的实现利用"池"的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间,使得并发 Web 应用快速响应请求
- POOLED需要配置的属性
- driver – 这是 JDBC 驱动的 Java 类的完全限定名(并不是JDBC驱动中可能包含的数据源类)
- url – 这是数据库的 JDBC URL 地址。
- username – 登录数据库的用户名。
- password – 登录数据库的密码。
- defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
- poolMaximumActiveConnections – 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10
- poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
- poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
- poolTimeToWait – 这是一个底层设置,如果获取连接花费的相当长的时间,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒)。
- poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中并准备接受请求。默认是"NO PING QUERY SET",这会导致多数数据库驱动失败时带有一个恰当的错误消息。
- poolPingEnabled – 是否启用侦测查询。若开启,也必须使用一个可执行的 SQL 语句设置poolPingQuery 属性(最好是一个非常快的 SQL),默认值:false。
- poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的使用频度。这可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。
## MyBatis 第二种内建数据源类型JNDI
- JNDI– 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源配置只需要两个属性
- initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么 data_source 属性将会直接从 InitialContext 中寻找。
- data_source – 这是引用数据源实例位置的上下文的路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
- JNDI和其他数据源配置类似,可以通过添加前缀"env."直接把属性传递给初始上下文
env.encoding=UTF8
会在初始上下文(InitialContext)实例化时往它的构造方法传递值为 UTF8 的 encoding 属性- 通过需要实现接口 org.apache.ibatis.datasource.DataSourceFactory , 也可使用任何第三方数据源
public interface DataSourceFactory {
void setProperties(Properties props);
DataSource getDataSource();
}
MyBatis XML映射文件
- MyBatis 强大的映射语句,MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。
- SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序)
- cache – 给定命名空间的缓存配置。
- cache-ref – 其他命名空间缓存配置的引用。
- resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
- sql – 可被其他语句引用的可重用语句块。
- insert – 映射插入语句
- update – 映射更新语句
- delete – 映射删除语句
- select – 映射查询语句
- select 元素是非常简单的
<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{
id}
</select>
- 这个语句被称作 selectPerson,接受一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值便是结果行中的对应值。
参数符号:#{id}
告诉 MyBatis 创建一个预处理语句参数,通过 JDBC,这样的一个参数在 SQL 中会由一个 “?” 来标识,并被传递到一个新的预处理语句中
Mybatis select , insert, update 和 delete元素属性的配置
<select
id="selectPerson"
parameterType="int"
parameterMap="deprecated"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10000"
fetchSize="256">
<insert
id="insertAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
keyProperty=""
keyColumn=""
useGeneratedKeys=""
timeout="20">
<update
id="updateAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20">
<delete
id="deleteAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20">
- id ;在命名空间中唯一的标识符,可以被用来引用这条语句。
- parameterType,传入这条语句的参数类的完全限定名或别名,此属性可选
- resultType,返回的期望类型的类的完全限定名或别名。如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。
- resultMap,外部 resultMap 的命名引用。
- flushCache,将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
- useCache,将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
- timeout,在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
- fetchSize,每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。
MyBatis resultMap元素
- resultMap 是 MyBatis 中最复杂的元素,主要用于解决实体类属性名与数据库表中字段名不一致的情况,可以将查询结果映射成实体对象
- resultMap元素的构成
<resultMap id="" type="">
<constructor><!-- 类再实例化时用来注入结果到构造方法 -->
<idArg/><!-- ID参数,结果为ID -->
<arg/><!-- 注入到构造方法的一个普通结果 -->
</constructor>
<id/><!-- 用于表示哪个列是主键 -->
<result/><!-- 注入到字段或JavaBean属性的普通结果 -->
<association property=""/><!-- 用于一对一关联 -->
<collection property=""/><!-- 用于一对多、多对多关联 -->
<discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
<case value=""/><!-- 基于某些值的结果映射 -->
</discriminator>
</resultMap>
<resultMap> 元素的 type 属性表示需要的 POJO,id 属性是 resultMap 的唯一标识。
- constructor用于配置构造方法。当一个 POJO 没有无参数构造方法时使用。
- id表示那个列是主键,允许福哦个主键,多个主键称为联合主键
- result用于表示 SQL 列名的映射关系。
- id 和 result 元素都有以下属性
- property属性,表示映射到列结果的字段或属性,如果 POJO 的属性和 SQL 列名(column元素)是相同的,那么 MyBatis 就会映射到 POJO 上
- column对应的SQL 列
- javaType配置 Java 类型。可以是特定的类完全限定名或 MyBatis 上下文的别名
- jdbcType配置数据库类型
- typeHandler类型处理器。允许你用特定的处理器来覆盖 MyBatis 默认的处理器
- 一条 SQL 查询语句执行后会返回结果集,结果集有两种存储方式,即使用 Map 存储和使用 POJO 存储。
Mybatis使用Map存储结果集
- 任何 select 语句都可以使用 Map 存储
<!-- 查询所有网站信息存到Map中 -->
<select id="selectAllWebsite" resultType="map">
select * from website
</select>
- 在 WebsiteMapper 接口中添加以下方法。
public List<Map<String,Object>> selectAllWebsite();
- Map 的 key 是 select 语句查询的字段名(必须完全一样),而 Map 的 value 是查询返回结果中字段对应的值,一条记录映射到一个 Map 对象中。
- 使用 Map 存储结果集很方便,但可读性稍差,所以一般推荐使用 POJO 的方式。
Mybatis使用POJO存储结果集
- 因为 MyBatis 提供了自动映射,所以使用 POJO 存储结果集是最常用的方式。但有时候需要更加复杂的映射或级联,这时就需要使用 select 元素的 resultMap 属性配置映射集合。
修改 Website 类
package net.biancheng.po;
import java.util.Date;
public class Website {
private int id;
private String uname;
private String url;
private int age;
private String country;
private Date createtime;
/* setter和getter方法*/
@Override
public String toString() {
return "Website[id=" + id + ",uname=" + uname + ",url=" + url + ",age=" + age + ",country=" + country
+ ",createtime=" + createtime + "]";
}
}
WebsiteMapper.xml
<!--使用自定义结果集类型 -->
<resultMap type="net.biancheng.po.Website" id="myResult">
<!-- property 是 net.biancheng.po.Website 类中的属性 -->
<!-- column是查询结果的列名,可以来自不同的表 -->
<id property="id" column="id" />
<result property="uname" column="name" />
</resultMap>
- resultMap 元素的属性 id 代表这个 resultMap 的标识,type 标识需要映射的 POJO。
- 可以使用 MyBatis 定义好的类的别名或自定义类的全限定名。
- 使用 property 元素指定 Website 的属性名称 uname,column 表示数据库中 website 表的 SQL 列名 name,将 POJO 和 SQL 的查询结果一 一对应。
WebsiteMapper.xml 中 select 元素配置代码
- 可以发现 SQL 语句的列名和 myResult 中的 column 一一对应。
<select id="selectAllWebsite" resultMap="myResult">
select id,name,url from website
</select>
resultType和resultMap的区别
- MyBatis 的每一个查询映射的返回类型都是 resultMap,当提供的返回类型是 resultType 时,MyBatis 会自动把对应的值赋给 resultType 所指定对象的属性
- 当提供的返回类型是 resultMap 时,MyBatis 会将数据库中的列数据复制到对象的相应属性上,可用于复制查询。
- resultMap 和 resultType 不能同时使用。
MyBatis注解
-
MyBatis注解主要分为三大类:SQL语句映射,结果集映射和关系映射
-
SQL语句映射
-
@Insert:实现新增功能
@Insert("insert into user(id,name) values(#{id},#{name})") public int insert(User user);
-
@Select:实现查询功能
@Select("Select * from user") @Results({ @Result(id = true, column = "id", property = "id"), @Result(column = "name", property = "name"), @Result(column = "sex", property = "sex"), @Result(column = "age", property = "age") }) List<User> queryAllUser();
-
@SelectKey:插入后,获取id的值,MySQL 在插入一条数据后,使用 select last_insert_id() 可以获取到自增 id 的值。
@Insert("insert into user(id,name) values(#{id},#{name})") @SelectKey(statement = "select last_insert_id()", keyProperty = "id", keyColumn = "id", resultType = int,before = false) public int insert(User user);
-
@SelectKey 各个属性含义如下:
- statement:表示要运行的 SQL 语句;
- keyProperty:可选项,表示将查询结果赋值给代码中的哪个对象;
- keyColumn:可选项,表示将查询结果赋值给数据表中的哪一列;
- resultType:指定 SQL 语句的返回值
- before:默认值为 true,在执行插入语句之前,执行 select last_insert_id()。值为 flase,则在执行插入语句之后,执行 select last_insert_id()。
-
@Insert:实现插入功能
@Insert("insert into user(name,sex,age) values(#{name},#{sex},#{age}") int saveUser(User user);
-
@Update:实现更新功能
@Update("update user set name= #{name},sex = #{sex},age =#{age} where id = #{id}") void updateUserById(User user);
-
@Delete:实现删除功能
@Delete("delete from user where id =#{id}") void deleteById(Integer id);
-
@Param 用于在 Mapper 接口中映射多个参数。@Param 中的 value 属性可省略,用于指定参数的别名。
int saveUser(@Param(value="user") User user,@Param("name") String name,@Param("age") Int age);
-
-
结果集映射:@Result、@Results、@ResultMap 是结果集映射的三大注解。
-
声明结果集映射关系代码
@Select({ "select id, name, class_id from student"}) @Results(id="studentMap", value={ @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true), @Result(column="name", property="name", jdbcType=JdbcType.VARCHAR), @Result(column="class_id ", property="classId", jdbcType=JdbcType.INTEGER) }) List<Student> selectAll();
-
@Results 各个属性的含义:
- id:表示当前结果集声明的唯一标识;
- value:表示结果集映射关系;
- @Result:代表一个字段的映射关系。其中,column 指定数据库字段的名称,property 指定实体类属性的名称,jdbcType 数据库字段类型,id 为 true 表示主键,默认 false。
-
可使用 @ResultMap 来引用映射结果集,其中 value 可省略。
@Select({ "select id, name, class_id from student where id = #{id}"}) @ResultMap(value="studentMap") Student selectById(Integer id);
-
关系映射:
-
@one:用于一对一关系映射
@Select("select * from student") @Results({ @Result(id=true,property="id",column="id"), @Result(property="name",column="name"), @Result(property="age",column="age"), @Result(property="address",column="address_id",one=@One(select="net.biancheng.mapper.AddressMapper.getAddress")) }) public List<Student> getAllStudents();
-
@many:用于一对多关系映射
@Select("select * from t_class where id=#{id}") @Results({ @Result(id=true,column="id",property="id"), @Result(column="class_name",property="className"), @Result(property="students", column="id", many=@Many(select="net.biancheng.mapper.StudentMapper.getStudentsByClassId")) }) public Class getClass(int id);
-
MyBatis关联查询
- 级联关系是一个数据库实体的概念,有 3 种级联关系,分别是一对一级联、一对多级联以及多对多级联。例如,一个角色可以分配给多个用户,也可以只分配给一个用户。大部分场景下,我们都需要获取角色信息和用户信息
- 在级联中存在 3 种对应关系。
- 一对多的关系,如角色和用户的关系。一个班级有多个学生
- 一对一的关系。一个学生都对应一个学号
- 多对多的关系,一个角色可以对应多个用户,但是一个用户可以兼任多个角色。
- 级联的优点是获取关联数据十分便捷。但是级联过多会增加系统的复杂度,同时降低系统的性能,此增彼减。所以记录超过 3 层时,就不要考虑使用级联了,因为这样会造成多个对象的关联,导致系统的耦合、负载和难以维护。
MyBatis一对一关联查询
- 一对一级联关系,人与身份证是一对一的级联关系。
- 在 MyBatis 中,通过
<resultMap>
元素的子元素<association>
处理一对一级联关系。
<association property="studentCard" column="cardId"
javaType="net.biancheng.po.StudentCard"
select="net.biancheng.mapper.StudentCardMapper.selectStuCardById" />
<association>
元素中通常使用以下属性。- property:指定映射到实体类的对象属性。
- column:指定表中对应的字段(即查询返回的列名)。
- javaType:指定映射到实体对象属性的类型。
- select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。
- 一对一关联查询可采用单步查询和分步查询
- 单步查询,通过关联查询实现
- 分步查询,通过两次或多次查询,为一对一关系的实体 Bean 赋值
示例
MyBatis 一对一关联查询处理过程
- 在数据库已经准备的情况下
- 创建数据表对应的持久化类 Student 和 StudentCard。
- Student 的代码
package net.biancheng.po; public class Student { private int id; private String name; private int sex; private StudentCard studentCard; /*省略setter和getter方法*/ @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", studentCard=" + studentCard + "]"; } }
- StudentCard
package net.biancheng.po; import java.util.Date; public class StudentCard { private int id; private int studentId; private Date startDate; private Date endDate; /*省略setter和getter方法*/ @Override public String toString() { return "StudentCard [id=" + id + ", studentId=" + studentId + "]"; } }
- Student 的代码
MyBatis 一对一关联查询的分步查询处理过程
-
新建 StudentCardMapper 类
package net.biancheng.mapper; import net.biancheng.po.StudentCard; public interface StudentCardMapper { public StudentCard selectStuCardById(int id); }
-
StudentCardMapper.xml 对应映射 SQL 语句
<mapper namespace="net.biancheng.mapper.StudentCardMapper"> <select id="selectStuCardById" resultType="net.biancheng.po.StudentCard"> SELECT * FROM studentCard WHERE id = #{ id} </select> </mapper>
-
StudentMapper 类
package net.biancheng.mapper; import net.biancheng.po.Student; public interface StudentMapper { public Student selectStuById1(int id); public Student selectStuById2(int id); }
-
StudentMapper.xml
<mapper namespace="net.biancheng.mapper.StudentMapper"> <!-- 一对一根据id查询学生信息:级联查询的第一种方法(嵌套查询,执行两个SQL语句) --> <resultMap type="net.biancheng.po.Student" id="cardAndStu1"> <id property="id" column="id" /> <result property="name" column="name" /> <result property="sex" column="sex" /> <!-- 一对一级联查询 --> <association property="studentCard" column="cardId" javaType="net.biancheng.po.StudentCard" select="net.biancheng.mapper.StudentCardMapper.selectStuCardById" /> </resultMap> <select id="selectStuById1" parameterType="Integer" resultMap="cardAndStu1"> select * from student where id=#{ id} </select> </mapper>
-
测试代码
public class Test { public static void main(String[] args) throws IOException { InputStream config = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config); SqlSession ss = ssf.openSession(); Student stu = ss.getMapper(StudentMapper.class).selectStuById1(2); System.out.println(stu); } }
单步查询
-
在 StudentMapper.xml 中添加
<resultMap type="net.biancheng.po.Student" id="cardAndStu2"> <id property="id" column="id" /> <result property="name" column="name" /> <result property="sex" column="sex" /> <!-- 一对一级联查询 --> <association property="studentCard" javaType="net.biancheng.po.StudentCard"> <id property="id" column="id" /> <result property="studentId" column="studentId" /> </association> </resultMap> <select id="selectStuById2" parameterType="Integer" resultMap="cardAndStu2"> SELECT s.*,sc.studentId FROM student s,studentCard sc WHERE s.cardId = sc.id AND s.id=#{ id} </select>
-
在 StudentMapper 中添加
public Student selectStuById2(int id);