Mybatis原理及使用
Mybatis
配置:
1、引入mybatis-3.1.1.jar
2、新建conf.xml放到src下
conf.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="url" value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/test/userMapper.xml" />
</mappers>
</configuration>
其中environments是环境,可以包含多个environment,默认使用default属性对应的。
transactionManager是事务管理配置,可以是JDBC或者MANAGED。
JDBC表示事务用jdbc来管理
MANAGED表示事务交给spring等容器管理。
dataSource type="POOLED"是使用连接池。
mappers指定映射文件的位置。
3、创建实体类
package com.test;
public class User {
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
4、创建映射文件
/MybatisDemo/src/com/test/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="com.test.userMapper">
<select id="queryUser" parameterType="int" resultType="com.test.User">
select * from users where id=#{aaa}
</select>
</mapper>
其中#{aaa}是占位符,由于只有一个参数,所以名字可以任意取,这里是aaa。
5、客户端调用
package com.test;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Test {
public static void main(String[] args) {
// 取得conf.xml总体配置文件的输入流
InputStream is = Test.class.getClassLoader().getResourceAsStream("conf.xml");
// 构建sessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
// 取得session
SqlSession sqlSession = sessionFactory.openSession();
// statement对应userMapper.xml中的namesapce+标签id
String statement = "com.test.userMapper.queryUser";
// 查询
User user = sqlSession.selectOne(statement, 1);
System.out.println(user.getName()); // 输出Tom
}
}
其中statement也可以是"queryUser" 短名称,它会自动去找命名空间,但这样不太好,如果2个命名空间都有id为queryUser的标签,这里就会报错,因此还是建议使用全名。
增删改查操作
userMapper.xml配置:
<!-- 新增 ,parameterType指定传入参数类型-->
<insert id="addUser" parameterType="com.test.User">
insert into users(name, age)
values (#{name}, #{age})
</insert>
<!-- 更新 ,parameterType指定传入参数类型-->
<update id="updateUser" parameterType="com.test.User">
update users
set name=#{name},age=#{age}
where id=#{id}
</update>
<!-- 删除,parameterType指定传入参数类型-->
<delete id="deleteUser" parameterType="int">
delete from users
where id=#{id}
</delete>
<!-- 查询列表,resultType指定返回列表的元素类型-->
<select id="queryAllUsers" resultType="com.test.User">
select * from users
</select>
增删改查的标签对应不同的sql语句,其实标签只是为了可读性,运行的时候只会看里面的语句,不会因为是update标签就一定执行update操作,例如update标签里面可以使insert语句,执行insert操作。标签起一定的检查作用,例如update标签就必须配置parameterType属性,否则运行报错。
对应测试代码:
@Test
public void testInsert() {
SqlSession sqlSession = MybatisUtils.getFactory().openSession();
String statement = "com.test.userMapper.addUser";
User user = new User();
user.setName("hello");
user.setAge(99);
sqlSession.insert(statement, user);
sqlSession.commit();
}
@Test
public void testUpdate() {
SqlSession sqlSession = MybatisUtils.getFactory().openSession();
String statement = "com.test.userMapper.updateUser";
User user = new User();
user.setName("test2");
user.setAge(105);
user.setId(2);
sqlSession.update(statement, user);
sqlSession.commit();
}
@Test
public void testDelete() {
SqlSession sqlSession = MybatisUtils.getFactory().openSession();
String statement = "com.test.userMapper.deleteUser";
sqlSession.delete(statement, 4);
sqlSession.commit();
}
@Test
public void testQueryAllUsers() {
SqlSession sqlSession = MybatisUtils.getFactory().openSession();
String statement = "com.test.userMapper.queryAllUsers";
List<User> users = sqlSession.selectList(statement);
System.out.println(users);
}
其中sqlSession.commit();是提交事务,openSession()取得的session默认不自动提交事务,如果是openSession(true)会自动提交
使用注解方式(很少用,只用于简单查询的情况)
1、创建接口
package com.test;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
public interface UserMapper {
@Insert("insert into users(name, age) values (#{name}, #{age})")
public int insertUser(User user);
@Update("update users set name=#{name},age=#{age} where id=#{id}")
public int updateUser(User user);
@Delete("delete from users where id=#{id}")
public int deleteUser(int id);
@Select("select * from users")
public List<User> queryAllUsers();
@Select("select * from users where id=#{aaa}")
public User queryById(int id);
}
2、conf.xml引入
<mapper class="com.test.UserMapper"/>
3、测试代码
@Test
public void testAnnotation() {
SqlSession sqlSession = MybatisUtils.getFactory().openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
System.out.println(userMapper.queryAllUsers());
}
其实就是用UserMapper接口代替了xml文件。
用log4j打印Sql语句等日志
1、引入log4j-1.2.16.jar
2、src下新建log4j.xml(也可以是log4j.properties),内容:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="debug" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
此时即可打印出sql语句:
用resultMap映射实体类属性与表字段名
如果数据库表字段名与实体类属性名不一致,则需要建立映射,一种方法是select语句中为字段起别名,如:
select user_id as id from users
另一种是使用mybatis的resultMap映射,如:
<!-- 查询列表,resultType指定返回列表的元素类型-->
<select id="queryAllUsers" resultMap="UserResultMap">
select * from users
</select>
<!-- 映射实体类属性名与表字段名 -->
<resultMap type="User" id="UserResultMap">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<result property="age" column="user_age"/>
</resultMap>
其中resultMap="UserResultMap"与resultMap标签的id对应。
resultMap标签内部的id标签和result标签可以互换,用id或者result只是为了方便阅读,一般id就是主键。
用association标签处理一对一、多对一封装(封装对象属性)
association在resultMap标签内,用于封装实体类属性的映射,当实体对象间有引用关系时,可以用association处理,如:
实体类
public class Teacher {
private int id;
private String name;
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 class Classes {
private int id;
private String name;
private Teacher teacher;
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 Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
这里Classes类有Teacher属性。
关联表查询方式(也叫嵌套结果)
通过关联表查询,一条sql查询出结果,如:
映射文件配置:
<select id="queryClasses" resultMap="ClassesResultMap">
select * from class a,teacher b
where a.teacher_id=b.t_id
</select>
<!-- 映射实体类属性名与表字段名 -->
<resultMap type="Classes" id="ClassesResultMap">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
其中association用于封装对象属性,property是属性名称,javaType是封装后的类型,需要跟property属性的类型一致。
association内部的id和result就是Teacher的属性,association就相当于Classes中Teacher属性的映射。
多次查询方式(也叫嵌套查询)
先查询出Classes信息,然后再查Classes对应的Teacher信息,如:
<select id="queryTeacher" parameterType="int" resultMap="TeacherResultMap">
select * from teacher where t_id=#{id}
</select>
<select id="queryClasses" resultMap="ClassesResultMap">
select * from class
</select>
<!-- 映射实体类属性名与表字段名 -->
<resultMap type="Classes" id="ClassesResultMap">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" select="queryTeacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
<resultMap type="Teacher" id="TeacherResultMap">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</resultMap>
这里的select="queryTeacher"会去找queryTeacher标签查询Teacher,传入column="teacher_id"列对应的值(column指定的相当于外键字段)
用collection标签处理一对多封装(封装List、Set等属性)
实体类
public class Classes {
private int id;
private String name;
private List<Student> students;
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 List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
public class Student {
private int id;
private String name;
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;
}
}
Classes中有个List<Student>属性。
关联表查询方式(也叫嵌套结果)
Mapper配置:
<select id="queryClasses" resultMap="ClassesResultMap">
select * from class c, student s
where c.c_id=s.class_id
</select>
<!-- 映射实体类属性名与表字段名 -->
<resultMap type="Classes" id="ClassesResultMap">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<collection property="students" ofType="Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
其中collection属性表示需要映射的属性是个集合,ofType="Student"表示集合元素的类型,需要跟property="students"对应的属性元素类型一致。
这里虽然查询语句会查询出多条,但会根据classes分组,自动把多条classes相同的存到同一个classes对象,然后多条的Students封装成list。
多次查询方式(也叫嵌套查询)
Mapper配置:
<select id="queryClasses" resultMap="ClassesResultMap">
select * from class
</select>
<select id="queryStudent" parameterType="int" resultMap="StudentResultMap">
select * from student where s_id=#{id}
</select>
<!-- 映射实体类属性名与表字段名 -->
<resultMap type="Classes" id="ClassesResultMap">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<collection property="students" column="c_id" select="queryStudent">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
<resultMap type="Student" id="StudentResultMap">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</resultMap>
这种也是发多次查询,其中column="c_id"是传给子查询的参数。
多对多关系处理
多对多可以转换为两个一对多,因为只会根据一方查了另一个方。
使用关联表查询然后用一对多关系处理即可,如:
表student:
表class
关联表class_student
映射配置:
<mapper namespace="com.test3.mapper">
<select id="queryClasses" resultMap="ClassesResultMap">
select a.id, a.name as className, c.id as studentId, c.name as studentName
from class a
left join class_student b on a.id=b.class_id
left join student c on c.id=b.student_id
</select>
<!-- 映射实体类属性名与表字段名 -->
<resultMap type="Classes" id="ClassesResultMap">
<id property="id" column="id"/>
<result property="name" column="className"/>
<collection property="students" ofType="Student">
<id property="id" column="studentId"/>
<result property="name" column="studentName"/>
</collection>
</resultMap>
</mapper>
用left join时,即使class表的每条记录即使在class_student没有对应,查询结果中也至少会有一条class的记录,只是student的list是空。
like查询
mapper.xml配置:
<select id="queryUser" parameterType="ConditionUser" resultType="User">
select * from users
where
name like #{name}
</select>
传参:
ConditionUser conditionUser = new ConditionUser();
String name = "o";
conditionUser.setName("%" + name + "%");
List<User> userList = session.selectList("com.testlike.mapper.queryUser", conditionUser);
System.out.println("userList=" + userList);
百分号写在了实际参数里。
Mybatis使用存储过程
使用很少,暂不研究
Mybatis一级、二级缓存
mybatis支持一级、二级缓存,一级缓存基SqlSession,二级缓存基于配置文件,增加<cache/>即可开启二级缓存,不过使用很少,暂不研究
Mybatis整合spring
1、添加jar包:
【mybatis】
mybatis-3.2.0.jar
mybatis-spring-1.1.1.jar
log4j-1.2.17.jar
【spring】
spring-aop-3.2.0.RELEASE.jar
spring-beans-3.2.0.RELEASE.jar
spring-context-3.2.0.RELEASE.jar
spring-core-3.2.0.RELEASE.jar
spring-expression-3.2.0.RELEASE.jar
spring-jdbc-3.2.0.RELEASE.jar
spring-test-3.2.4.RELEASE.jar
spring-tx-3.2.0.RELEASE.jar
aopalliance-1.0.jar
cglib-nodep-2.2.3.jar
commons-logging-1.1.1.jar
【MYSQL驱动包】
mysql-connector-java-5.0.4-bin.jar
或者maven配置:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>mybatisspring</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mybatisspring</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.2.6.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.4</version>
</dependency>
</dependencies>
</project>
2、创建实体类,如:
public class User {
private int id;
private String name;
private Date birthday;
private double salary;
//set,get方法
}
创建表:
user_id INT AUTO_INCREMENT PRIMARY KEY,
3、创建Dao的接口
4、创建mapper映射文件usermapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.dmall.mapper.UserMapper">
<insert id="save" parameterType="User">
insert into s_user(user_name, user_birthday, user_salary)
values (#{name}, #{birthday}, #{salary})
<update id="update" parameterType="User">
set user_name=#{name}, user_birthday=#{birthday}, user_salary=#{salary}
<delete id="delete" parameterType="int">
<select id="findById" parameterType="int" resultType="User">
select user_id id, user_name name, user_birthday birthday, user_salary salary
<select id="findAll" resultType="User">
select user_id id, user_name name, user_birthday birthday, user_salary salary
其中的namespace必须与接口的全名一致,标签的id必须跟接口中的方法名一致。
5、创建spring配置文件spring_config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<!-- 2.配置mysql的SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<property name="typeAliasesPackage" value="com.dmall.domain"></property>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
<tx:annotation-driven transaction-manager="txManager"/>
<!-- 5.配置扫描mapper映射文件的类MapperScannerConfigurer -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dmall.mapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
其中第5步是让spring自动扫描包下的mybatis的mapper配置文件,通过反射dao的接口类自动创建实现。如果不用spring自动创建dao的实现(接口绑定的方式),则可以不用配置第5步。
这里使用的是注解式事务(@Transactional加到方法上)。
<aop:config proxy-target-class="true">
<aop:pointcut id="txServiceMethods" expression="execution(* com.dmall.ledger.service..*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txServiceMethods" />
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:method name="*" propagation="REQUIRED" />
6、创建测试类
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.dmall.mapper.UserMapper;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring_config.xml")
其中的@RunWith(SpringJUnit4ClassRunner.class)表示用spring的测试框架,这样可以不用手动去加载spring的xml配置。
@ContextConfiguration("/spring_config.xml")前面的斜杠表示src的根路径。
这样通过@Autowired就可以直接注入spring的对象进行测试,省了很多事。
7、说明
跟不使用spring取得SqlSession类似,只不过它是通过InputStream的方式通过读取配置文件取得数据源。
但一般不会将mapper.xml放到实体的包路径下,而是单独建一个目录专门放mapper.xml,因此一般情况下还是会有mybatis配置文件。
如果有其他特殊配置,也可以使用conf.xml里面的配置参数。
此时启用conf.xml的方式是将路径配置到SessionFactory的configLocation属性:
附:测试用表sql
CREATE TABLE users(id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20), age INT);
INSERT INTO users(NAME, age) VALUES('Tom', 12);
INSERT INTO users(NAME, age) VALUES('Jack', 11);
order_id INT PRIMARY KEY AUTO_INCREMENT,
INSERT INTO orders(order_no, order_price) VALUES('aaaa', 23);
INSERT INTO orders(order_no, order_price) VALUES('bbbb', 33);
INSERT INTO orders(order_no, order_price) VALUES('cccc', 22);
t_id INT PRIMARY KEY AUTO_INCREMENT,
c_id INT PRIMARY KEY AUTO_INCREMENT,
ALTER TABLE class ADD CONSTRAINT fk_teacher_id FOREIGN KEY (teacher_id) REFERENCES teacher(t_id);
INSERT INTO teacher(t_name) VALUES('LS1');
INSERT INTO teacher(t_name) VALUES('LS2');
INSERT INTO class(c_name, teacher_id) VALUES('bj_a', 1);
INSERT INTO class(c_name, teacher_id) VALUES('bj_b', 2);
s_id INT PRIMARY KEY AUTO_INCREMENT,
INSERT INTO student(s_name, class_id) VALUES('xs_A', 1);
INSERT INTO student(s_name, class_id) VALUES('xs_B', 1);
INSERT INTO student(s_name, class_id) VALUES('xs_C', 1);
INSERT INTO student(s_name, class_id) VALUES('xs_D', 2);
INSERT INTO student(s_name, class_id) VALUES('xs_E', 2);
INSERT INTO student(s_name, class_id) VALUES('xs_F', 2);
id int primary key auto_increment,
insert into d_user(name,age) values('Tom',12);
insert into d_user(name,age) values('Bob',13);
insert into d_user(name,age) values('Jack',18);
id INT PRIMARY KEY AUTO_INCREMENT,
INSERT INTO c_user(NAME, age) VALUES('Tom', 12);
INSERT INTO c_user(NAME, age) VALUES('Jack', 11);
user_id INT AUTO_INCREMENT PRIMARY KEY,
其他
conf.xml总体配置
mappers映射文件路径配置
<mapper resource="com/test/userMapper.xml" />
<mapper url="file:///D:/aaa/userMapper.xml" />
<mapper url="file:///home/kkk/userMapper.xml" />
使用properties文件
可以将数据库连接等配置信息放入properties文件,xml引入它,然后用$符号引用变量,如:
url=jdbc:mysql://localhost:3306/mybatis
<?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">
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${name}" />
<property name="password" value="${password}" />
<mapper resource="com/test/userMapper.xml" />
其中 <properties resource="db.properties"/>
是引用文件,${driver}对应properties的driver属性值。
properties可以有property属性,用于补充properties文件中不存在的属性,如果property属性跟properties文件有相同的,优先取properties文件的,如:
<properties resource="db.properties">
<property name="password" value="123453"/>
<property name="hello" value="hello123"/>
password此时还是优先取properties文件的password=123456
Properties properties = new Properties();
properties.setProperty("name", "aaa");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader, properties);
此时代码中的设置的name会以最高优先级的方式覆盖掉properties文件的name配置。
代码指定参数>properties文件配置>xml的property标签配置。
Settings配置全局参数
typeAliases起别名
<typeAlias type="com.test.User" alias="User"/>
<select id="queryUser" parameterType="int" resultType="User">
select * from users where id=#{aaa}
其中resultType的User就代表了com.test.User
<select id="queryUserName" parameterType="int" resultType="string">
select name from users where id=#{aaa}
这里用resultType="string"、resultType="String"、resultType="java.lang.String"都可以。
typeHandler自定义类型转换
数据库类型和java类型的转换都是通过实现TypeHandler接口的类实现的,如:
自定义类型处理器需要实现TypeHandler接口,继承TypeReference类。
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeReference;
public class ExampleTypeHandler extends TypeReference<String> implements TypeHandler<String> {
// TODO Auto-generated method stub
public String getResult(ResultSet rs, String columnName) throws SQLException {
// TODO Auto-generated method stub
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
// TODO Auto-generated method stub
public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
// TODO Auto-generated method stub
<properties resource="com/test/config.properties" />
<typeHandler handler="com.test.handler.ExampleTypeHandler" />
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${name}" />
<property name="password" value="${password}" />
<mapper resource="com/test/userMapper.xml" />
有现成的BaseTypeHandler抽象类实现了实现TypeHandler接口,继承了TypeReference类,可以继承BaseTypeHandler抽象类就行了。
ps:此种方式对全局所有的转换有效,如果只需要对单个sql查询结果进行转换,需要用resultMap标签配置,具体待研究。
environments配置多个环境
environments可配置多个数据库连接,如开发环境、生产环境,默认使用default属性指定的。构建SqlSessionFactory时可以手动指定,如:
有一个develop和一个product环境,默认使用development的。但代码中可以手动指定成product的:
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader, "product");
TracsanctionManager、Datasource待确定
使用接口绑定的方式
User user = sqlSession.selectOne("com.test.UserMapper.queryUser", 1);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryUser(1);
public User queryUser(int id);
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.test.UserMapper">
<select id="queryUser" parameterType="int" resultType="com.test.User">
select * from users where id=#{aaa}
这样做的好处是不用写namespace防止拼写错误。相当于把xml中的namespace和id映射到了接口全路径名和方法名上。然后sqlSession通过接口名和方法去xml中找对应。
范围和生命周期
一个请求线程一个SqlSession,或者每次操作数据库都创建,它是线程不安全的。
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
参数绑定
select标签
select标签的resultMap或者resultType必须有一个,表名返回类型如何封装,如果是map,则返回结果的key是字段名,value是字段值。如果有as,则key是as的别名。
insert、update、delete
insert返回数据库生成的主键id
使用insert标签的useGeneratedKeys、属性即可,如:
这里的keyProperty="id"中的id对应的是User实体类的属性名字(其实是setId方法)。
userGeneratedKeys默认是false,也就是不会返回主键id。
sql标签定义重复使用的代码段
动态sql
可以用类似于jstl的if、when、foreach等标签动态拼装sql语句
if标签
<select id="queryUser" parameterType="ConditionUser" resultType="User">
<if test="name != null and name != 'null'">
此时age传入0时,会认为跟''相等,进而不加where条件,因此对Integer类型判断时不要加age != ''条件,而只加age!=null即可排除空,否则会把0也一起排除。
choose标签
where标签
当name和age都为空时,如果不用where标签就会报sql错。
当name为空,age不为空时,不用where标签也会报sql错。
where标签会判断如果后面没有条件,就不加where关键字,如果后面条件以and、or开头,就把它去掉。
set标签
与where类似,用于解决sql拼接问题,set用在update里,如:
foreach标签
传入参数是一个map,foreach的collection通过"idsList"这个key取出map中的list,然后遍历。其中separator指定分隔符。item指定遍历的单个元素名字。
遍历Map
<update id="update" parameterType="SyncdataVo">
update ${syncdataVo.tableName} set
<foreach collection="syncdataVo.optMap.keys" item="key" separator="," >
${key}=#{syncdataVo.optMap[${key}]}
这里遍历传入map的key、value对应更新字段值。key作为字段名,value作为更新值。
源码分析
读取配置,创建Configuration,创建SessionFactory
Reader reader = Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
首先读取reader中的配置,封装成configuration。创建sessionFactory时,会将configuration传给sessionFactory。
创建SqlSession,传递configuration
SqlSession sqlSession = sessionFactory.openSession();
创建SqlSession时,会将configuration传给SqlSession。
基于Mapper方式的获取和回调
获取Mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
从SqlSession中获取Mapper,其实调用了configuration的getMapper。
configuration会去调用MapperRegistry. getMapper方法。
MapperRegistry通过目标接口名字找出MapperProxyFactory,然后调用MapperProxyFactory.newInstance创建。
ps: knownMappers是一个HashMap,key是接口类Class,value是MapperProxyFactory对象。
通过接口类Class为key找到对应的MapperProxyFactory,对应的MapperProxyFactory中就含有对应的接口。
MapperProxyFactory创建InvocationHandler的实现类MapperProxy的对象,然后根据目标接口创建jdk动态代理对象。
执行接口方法时回调MapperProxy的invoke方法
SqlSession的操作流程
不论是否采用Mapper接口方式,最后增删改查都是通过SqlSession来调用的,如:
SqlSession取得MappedStatement,调用Executor的query
Executor生成BoundSql,创建StatementHandler
Executor调用StatementHandler创建Statement,封装参数
Executor调用StatementHandler执行query
ResultSetHandler封装返回结果
Mybatis中使用的设计模式
CachingExecutor中使用了委托模式和装饰器模式,将所有操作都委托给一个成员变量SimpleExector去操作,而它在外面做了一层封装(装饰)。

浙公网安备 33010602011771号