MyBatis进阶
参考文档:
XML配置
参考文档:
XML映射文件
参考文档:
Java API
JAVA对象与复杂的表关系之间的映射案例
PS:具体的配置信息,可以参考
MyBatis入门这篇博客
该案例所对应的ER图和表设计图
-
配置SqlSessionFactory全局配置文件
<?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 type="com.pc.mybatiscomplex.User" alias="User"/>
<typeAlias type="com.pc.mybatiscomplex.Teacher" alias="Teacher"/>
<typeAlias type="com.pc.mybatiscomplex.Course" alias="Course"/>
</typeAliases>
<!-- 配置环境变量 -->
<environments default="development">
<!-- environment元素体中包含了事务管理和连接池的配置 -->
<environment id="development">
<!-- 事务管理器 -->
<!-- JDBC:使用JDBC的事务管理机制 -->
<!-- MANAGED:让Spring这样的容器来管理事务的生命周期-->
<transactionManager type="jdbc"/>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/cloud_study?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
PS:文件名为:confComplex.xml,这里配置了昵称,那么在该配置文件或者其包含的映射文件中,就能以配置好的昵称作为对应类的完全限定名的代替。
-
创建Bean对象类(POJO)
-
1 创建职员类
-
package com.pc.mybatiscomplex;
import java.util.List;
/**
* Created by switch on 16/9/13.
* 职员Bean(POJO)————与user表映射
*/
public class User {
private Integer id;
private String userName;
private String corp;
private List<Course> courses;
public User(Integer id, String userName, String corp) {
this.id = id;
this.userName = userName;
this.corp = corp;
}
public User(String userName, String corp) {
this.userName = userName;
this.corp = corp;
}
public Integer getId() {
return id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getCorp() {
return corp;
}
public void setCorp(String corp) {
this.corp = corp;
}
public List<Course> getCourses() {
return courses;
}
public void setCourses(List<Course> courses) {
this.courses = courses;
}
}
-
-
2 创建课程类
-
package com.pc.mybatiscomplex;
/**
* Created by switch on 16/9/13.
* 课程Bean(POJO)————与course表映射
*/
public class Course {
private Integer id;
private String courseName;
private Teacher teacher;
public Course(Integer id, String courseName, Teacher teacher) {
this.id = id;
this.courseName = courseName;
this.teacher = teacher;
}
public Course(String courseName, Teacher teacher) {
this.courseName = courseName;
this.teacher = teacher;
}
public Course() {
}
public Integer getId() {
return id;
}
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
-
-
3 创建讲师类
-
package com.pc.mybatiscomplex;
/**
* Created by switch on 16/9/13.
* 讲师Bean(POJO)————与teacher表映射
*/
public class Teacher {
private Integer id;
private String teacherName;
public Teacher(Integer id, String teacherName) {
this.id = id;
this.teacherName = teacherName;
}
public Teacher(String teacherName) {
this.teacherName = teacherName;
}
public Integer getId() {
return id;
}
public String getTeacherName() {
return teacherName;
}
public void setTeacherName(String teacherName) {
this.teacherName = teacherName;
}
}
-
创建映射器接口
package com.pc.mybatiscomplex;
/**
* Created by switch on 16/9/13.
* 职员操作接口
*/
public interface UserOp {
// 获取职员信息
public User getUser(Integer id);
}
-
配置mapper文件
<?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.pc.mybatiscomplex.UserOp">
<!-- 查询 -->
<!-- id:对应接口的方法名 -->
<!-- parameterType:参数类型 -->
<!-- resultMap:自定义结果集 -->
<!-- 该查询是根据职员id,查询该职员的基本信息及其选课的课程信息和对应的老师信息,采用的是左外连接的方式 -->
<select id="getUser" parameterType="integer" resultMap="UserMap">
SELECT
u.id AS userId,
userName,
courseName,
corp,
c.id AS courseId,
t.id AS teacherId,
teacherName
FROM user u LEFT OUTER JOIN UserCourse uc ON u.id = uc.user_id
LEFT OUTER JOIN course c ON c.id = uc.course_id
LEFT OUTER JOIN teacher t ON t.id = c.teacher_id
WHERE u.id = #{id};
</select>
<!-- 自定义结果集,id为UserMap -->
<!-- type:结果集类型 -->
<!-- 注意type采用的是昵称,而不是完全限定名 -->
<resultMap id="UserMap" type="User">
<!-- 通过构造器来构造User对象-->
<!-- 带有该参数列表的构造器,必须在Bean中存在 -->
<!-- MyBatis框架会按名自动注入 -->
<constructor>
<!-- idArg: 标记结果作为ID可以帮助提高整体效能,一般是标记主键-->
<idArg column="userId" javaType="Integer"/>
<!-- arg: 注入到构造方法的一个普通结果 -->
<arg column="userName" javaType="String"/>
<arg column="corp" javaType="String"/>
</constructor>
<!-- 集合,实现关系模型到对象模型的1对多的映射 -->
<!-- property:必须为其上层元素代表的对象的一个集合属性,并且名字必须一样 -->
<!-- ofType:集合中元素的类型 -->
<!-- PS:如果该元素添加javaType属性,其值将是集合类型,比如说ArrayList -->
<collection property="courses" ofType="Course">
<!-- id: 标记结果作为ID可以帮助提高整体效能,一般是标记主键-->
<id property="id" column="courseId"/>
<!-- result: 注入的一个普通结果 -->
<result property="courseName" column="courseName"/>
<!-- 关联,实现关系模型到对象模型的1对1的映射 -->
<!-- property:必须为其上层元素代表的对象的一个属性,并且名字必须一样 -->
<!-- javaType:属性类型 -->
<association property="teacher" javaType="Teacher">
<constructor>
<idArg column="teacherId" javaType="Integer"/>
<arg column="teacherName" javaType="String"/>
</constructor>
</association>
</collection>
</resultMap>
</mapper>
PS:文件名为:userMapperComplex.xml,这里select的返回结果是自定义结果集。关于自定义结果集的更详细文档可以在这篇博客最开始的位置找到。
-
在SqlSessionFactory配置文件中配置mapper映射器
<?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 type="com.pc.mybatiscomplex.User" alias="User"/>
<typeAlias type="com.pc.mybatiscomplex.Teacher" alias="Teacher"/>
<typeAlias type="com.pc.mybatiscomplex.Course" alias="Course"/>
</typeAliases>
<!-- 配置环境变量 -->
<environments default="development">
<!-- environment元素体中包含了事务管理和连接池的配置 -->
<environment id="development">
<!-- 事务管理器 -->
<!-- JDBC:使用JDBC的事务管理机制 -->
<!-- MANAGED:让Spring这样的容器来管理事务的生命周期-->
<transactionManager type="jdbc"/>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/cloud_study?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 配置映射器 -->
<mappers>
<mapper resource="mapper/userMapperComplex.xml"/>
</mappers>
</configuration>
-
测试结果
package com.pc.mybatiscomplex;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
/**
* Created by switch on 16/9/13.
* 测试类
*/
public class HelloMyBatisComplex {
public static void main(String[] args) {
// 1. 声明配置文件的目录
String resource = "confComplex.xml";
// 2. 加载应用配置文件
InputStream is = HelloMyBatisComplex.class.getClassLoader().getResourceAsStream(resource);
// 3. 创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
// 4. 获取Session————try-with-resource
try (SqlSession session = sessionFactory.openSession()) {
// 5. 获取操作类
UserOp userOp = session.getMapper(UserOp.class);
// 6. 完成查询操作
User user = userOp.getUser(1);
// 验证、输出结果
// 用户信息
System.out.println("--------用户信息--------");
System.out.println("职员ID:" + user.getId() + " 职员名:" + user.getUserName() + " 公司名:" + user.getCorp());
// 选课信息
System.out.println("--------选课信息--------");
for (Course course : user.getCourses()) {
System.out.println("课程ID:" + course.getId() + " 课程名:" + course.getCourseName() +
" 讲师ID:" + course.getTeacher().getId() + " 讲师名:" + course.getTeacher().getTeacherName());
}
}
}
}
参考项目结构
数据库表结构与数据
-- 职员表
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- 职员id
`userName` varchar(100) NOT NULL, -- 职员名
`corp` varchar(100) DEFAULT NULL, -- 公司
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO user(userName, corp) VALUES("张三", "A公司");
INSERT INTO user(userName, corp) VALUES("李四", "B公司");
INSERT INTO user(userName, corp) VALUES("王五", "C公司");
INSERT INTO user(userName, corp) VALUES("赵六", "B公司");
INSERT INTO user(userName, corp) VALUES("钱七", "A公司");
-- 讲师表
CREATE TABLE `teacher` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- 讲师id
`teacherName` varchar(100) DEFAULT NULL, -- 讲师名
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO teacher(teacherName) VALUES("张老师");
INSERT INTO teacher(teacherName) VALUES("李老师");
INSERT INTO teacher(teacherName) VALUES("王老师");
INSERT INTO teacher(teacherName) VALUES("赵老师");
INSERT INTO teacher(teacherName) VALUES("钱老师");
-- 课程表
CREATE TABLE `course` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- 课程id
`courseName` varchar(100) DEFAULT NULL, -- 课程名
`teacher_id` int(11) DEFAULT NULL, -- 讲师id
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO course(courseName, teacher_id) VALUES("MyBatis", 1);
INSERT INTO course(courseName, teacher_id) VALUES("JDBC", 2);
INSERT INTO course(courseName, teacher_id) VALUES("Spring", 1);
INSERT INTO course(courseName, teacher_id) VALUES("Hibernate", 3);
INSERT INTO course(courseName, teacher_id) VALUES("JAVA", 4);
INSERT INTO course(courseName, teacher_id) VALUES("Oracle", 5);
INSERT INTO course(courseName, teacher_id) VALUES("MySQL", 1);
INSERT INTO course(courseName, teacher_id) VALUES("Struts", 5);
-- 职员选课表
CREATE TABLE `UserCourse` (
`id` int(11) NOT NULL AUTO_INCREMENT, -- 选课id
`user_id` int(11) DEFAULT NULL, -- 职员id
`course_id` int(11) DEFAULT NULL, -- 课程id
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO UserCourse(user_id, course_id) VALUES(1, 1);
INSERT INTO UserCourse(user_id, course_id) VALUES(1, 3);
INSERT INTO UserCourse(user_id, course_id) VALUES(1, 5);
INSERT INTO UserCourse(user_id, course_id) VALUES(1, 7);
INSERT INTO UserCourse(user_id, course_id) VALUES(2, 2);
INSERT INTO UserCourse(user_id, course_id) VALUES(2, 4);
INSERT INTO UserCourse(user_id, course_id) VALUES(2, 6);
INSERT INTO UserCourse(user_id, course_id) VALUES(2, 8);
INSERT INTO UserCourse(user_id, course_id) VALUES(3, 1);
INSERT INTO UserCourse(user_id, course_id) VALUES(3, 2);
INSERT INTO UserCourse(user_id, course_id) VALUES(3, 5);
INSERT INTO UserCourse(user_id, course_id) VALUES(4, 3);
INSERT INTO UserCourse(user_id, course_id) VALUES(4, 5);
INSERT INTO UserCourse(user_id, course_id) VALUES(4, 7);
INSERT INTO UserCourse(user_id, course_id) VALUES(5, 1);
INSERT INTO UserCourse(user_id, course_id) VALUES(5, 4);
INSERT INTO UserCourse(user_id, course_id) VALUES(5, 6);
INSERT INTO UserCourse(user_id, course_id) VALUES(5, 7);
数据源(dataSource)
UNPOOLED
这个数据源的实现只是每次被请求时打开和关闭连接。虽然一点慢,它对在及时可用连接方面没有性能要求的简单应用程序是一个很好的选择。 不同的数据库在这方面表现也是不一样的,所以对某些数据库来说使用连接池并不重要,这个配置也是理想的。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:
- driver – 这是 JDBC 驱动的 Java 类的完全限定名(并不是JDBC驱动中可能包含的数据源类)。
- url – 这是数据库的 JDBC URL 地址。
- username – 登录数据库的用户名。
- password – 登录数据库的密码。
- defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
作为可选项,你也可以传递属性给数据库驱动。要这样做,属性的前缀为“driver.”,例如:
- driver.encoding=UTF8
这将通过DriverManager.getConnection(url,driverProperties)方法传递值为 UTF8 的 encoding 属性给数据库驱动。
POOLED
这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式。
除了上述提到 UNPOOLED 下的属性外,会有更多属性用来配置 POOLED 的数据源:
- poolMaximumActiveConnections – 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10,考虑到随着连接数的增加,性能可能达到拐点,不建议设置过大。
- poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。建议设置与poolMaximum相同。
- poolMaximumCheckoutTime – 获取连接时如果没有idelConnection,同时activeConnection达到最大值,则从activeConnection列表中第一个连接开始,检查时间是否超过该值,如果超过,则强制使其失效,返回该连接。由于SQL执行时间受服务器配置、表结构不同,建议设置为预期最大SQL执行时间,默认值:20000 毫秒(即 20 秒)
- poolTimeToWait – 这是一个底层设置,如果获取连接花费的相当长的时间,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒)。
- poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。建议使用select 1,开销小。
- poolPingEnabled – 是否启用侦测查询。若开启,也必须使用一个可执行的 SQL 语句设置 poolPingQuery 属性(最好是一个非常快的 SQL),默认值:false。
- poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的使用频度。这可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。建议小于服务器端超时时间,MySQL默认超时8小时。
案例
<!-- 配置数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/cloud_study?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="poolMaximumActiveConnections" value="50"/>
<property name="poolMaximumIdleConnections" value="50"/>
<property name="poolMaximumCheckoutTime" value="10000"/>
<property name="poolTimeToWait" value="60000"/>
<property name="poolPingEnabled" value="true"/>
<property name="poolPingQuery" value="select 1"/>
<property name="poolPingConnectionsNotUsedFor" value="25000000"/>
</dataSource>
JNDI
这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源配置只需要两个属性:
- initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么 data_source 属性将会直接从 InitialContext 中寻找。
- data_source – 这是引用数据源实例位置的上下文的路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给初始上下文。比如:
- env.encoding=UTF8
这就会在初始上下文(InitialContext)实例化时往它的构造方法传递值为
UTF8 的
encoding 属性。
-------------参考《网易云课堂.数据库开发》
-------------参考《MyBatis 3.4.1官方文档》