#MyBatis进阶
- 复杂Java对象与多张数据库表之间的映射关系
- 配置数据库连接池提高MyBatis对后端数据库访问的性能 ##更为复杂的E-R图 学员对课程是多对多关系,而教员与课程是一对多关系
-
- 学员 User
-
- 课程 Couser
-
- 教员 Author
以上实体对应的关系图如下:
以上关系对应的数据库表关系如下:
各个实体都具有对应的属性,但是由于User与Cousre是多对多关系,则我们还需要一个多对多关系表UserCourse,来保存User与Course的关系。由于Teacher与Course是一对多的关系,则我们在Course中添加Teacher的唯一标示。
##复杂对象关系
- Java对象
-
- 关联
-
- 容器
-
- 嵌套
##ResultMap 复杂Java关系映射解决,帮助我们复杂对象到多张数据库表的转换。
- ResultMap元素师MyBatis中最重要最强大的元素。
- 数据库永远不是你想要的或需要它们是什么样的。
- ResultMap可以实现复杂查询结果到复杂对象关联关系的转化。
##Constructor 通过构造方法的方式,对对象进行赋值建立映射关系,这种方法一般用于处理关联时,使用。
- 类在实例化时,用对象的构造函数注入到对象中:
-
- idArg - ID参数;标记结果作为ID可以帮助提高整体效能
-
- arg - 注入到构造方法的一个普通结果
##ResultMap通过构造函数实例 ###定义了MyBatis-conf.xml的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>
<environments default="development">
<environment id="development">
<!-- 事务管理 -->
<transactionManager type="jdbc">
<!--<property name="..." value="..."/>-->
</transactionManager>
<!-- 数据库连接数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.1.200/cloud_study"/>
<property name="username" value="root"/>
<property name="password" value="dVHJtG0T:pf*"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/hava/cloud_study/repository/userMapper.xml" />
</mappers>
</configuration>
###声明Java对象 ####User
public class User {
private int id;
private String userName;
private String corp;
private List<Course> courses;
...
//Getter and Setter
}
####Course
public class Course {
private int id;
private String courseName;
private Teacher teacher;
...
//Getter and Setter
}
####Teacher
public class Teacher {
private int id;
private String teacherName;
...
//Getter and Setter
}
###UserOp接口 我们通过获取用户的方式,来获取三张数据库表之间的连接查询,并建立三个对象之间的连接关系。
package com.hava.cloud_study.repository;
import com.hava.cloud_study.entity.User;
/**
* Created by zhanpeng on 09/10/2016.
*/
public interface UserOp {
public User getUser(int id);
}
###resultMap核心Mapper 在Mapper文件中添加resultMap的标签属性,id在所表示的这个resultMap将会被用在增持该查中设置中。type表示为要转换的对象结果。constructor标签下面表示的是该类的属性(非连接关系的属性),idArg表示为行记录的唯一id,arg表示一般属性。column必须和对象的属性名称相一致。javaType对应的Java的数据类型。
<?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.hava.cloud_study.repository.UserOp">
<select id="getUser" parameterType="int" resultMap="UserMap">
select u.id
as userId,userName,courseName,corp,c.id as courseId,teacher.id as
teacherId,teacherName from user u left
join
UserCourse uc on u.id
=uc.user_id left join course c on c.id =
uc.course_id left join teacher
on teacher.id = c.teacher_id where u.id
= #{id}
</select>
<resultMap id="UserMap"
type="com.hava.cloud_study.entity.User">
<constructor>
<idArg column="userId" javaType="int" />
<arg column="userName" javaType="String" />
<arg column="corp" javaType="String" />
</constructor>
<collection property="courses"
ofType="com.hava.cloud_study.entity.Course">
<id property="id" column="courseId" />
<result property="courseName" column="courseName" />
<association property="teacher" column="teacher_id"
javaType="com.hava.cloud_study.entity.Teacher">
<id property="id" column="teacherId" />
<result property="teacherName" column="teacherName" />
</association>
</collection>
</resultMap>
</mapper>
必须在User中加入构造函数,构造函数的属性必须和constructor一致。
###执行
package com.hava.cloud_study.service;
import java.io.InputStream;
import com.hava.cloud_study.entity.User;
import com.hava.cloud_study.repository.UserOp;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class HelloMyBatis {
public static void main(String[] args) {
// 1. 声明配置⽂件的⺫录渎职
String resource = "mybatis-conf.xml";
// 2. 加载应⽤配置⽂件
InputStream is = HelloMyBatis.class.getClassLoader()
.getResourceAsStream(resource);
// 3. 创建SqlSessonFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder()
.build(is);
// 4. 获取Session
SqlSession session = sessionFactory.openSession();
try {
// 5. 获取操作类
UserOp userOp = session.getMapper(UserOp.class);
// 6. 完成查询操作
User user = userOp.getUser(25);
System.out.println(user.getId() + " " + user.getUserName() + " ");
System.out.println(user.getCourses().get(0).getCourseName() +" ");
System.out.println(user.getCourses().get(0).getTeacher().getTeacherName());
} finally {
// 7.关闭Session
session.close();
}
}
}
###执行结果
25 ZhanPeng
Java
OldTeacher
##Collection 容器 User当中的List注入,也是通过resultMap,通过collection的标签来实现。
- 实现一对多的关联
-
- id - 一个ID结果;标记结果作为ID可以帮助提高整体效能
-
- result - 注入到字段或者JavaBean属性的普通结果 - 普通属性注入
Collection 标签中property必须和对象的属性一致。
<?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.hava.cloud_study.repository.UserOp">
<select id="getUser" parameterType="int" resultMap="UserMap">
select u.id
as userId,userName,courseName,corp,c.id as courseId,teacher.id as
teacherId,teacherName from user u left
join
UserCourse uc on u.id
=uc.user_id left join course c on c.id =
uc.course_id left join teacher
on teacher.id = c.teacher_id where u.id
= #{id}
</select>
<resultMap id="UserMap"
type="com.hava.cloud_study.entity.User">
<constructor>
<idArg column="userId" javaType="int" />
<arg column="userName" javaType="String" />
<arg column="corp" javaType="String" />
</constructor>
<!-- 定义对象为List的属性 -->
<collection property="courses"
ofType="com.hava.cloud_study.entity.Course">
<id property="id" column="courseId" />
<result property="courseName" column="courseName" />
<association property="teacher" column="teacher_id"
javaType="com.hava.cloud_study.entity.Teacher">
<id property="id" column="teacherId" />
<result property="teacherName" column="teacherName" />
</association>
</collection>
</resultMap>
</mapper>
##Association 实现对象与对象之间的连接
- 实现复杂类型之间的关联
-
- id - 一个ID结果;标记结果作为ID可以帮助提高整体效能
-
- result - 注入到字段或者JavaBean属性的普通结果 - 普通属性注入
<?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.hava.cloud_study.repository.UserOp">
<select id="getUser" parameterType="int" resultMap="UserMap">
select u.id
as userId,userName,courseName,corp,c.id as courseId,teacher.id as
teacherId,teacherName from user u left
join
UserCourse uc on u.id
=uc.user_id left join course c on c.id =
uc.course_id left join teacher
on teacher.id = c.teacher_id where u.id
= #{id}
</select>
<resultMap id="UserMap"
type="com.hava.cloud_study.entity.User">
<constructor>
<idArg column="userId" javaType="int" />
<arg column="userName" javaType="String" />
<arg column="corp" javaType="String" />
</constructor>
<!-- 定义对象为List的属性 -->
<collection property="courses"
ofType="com.hava.cloud_study.entity.Course">
<id property="id" column="courseId" />
<result property="courseName" column="courseName" />
<!-- 一对一 -->
<association property="teacher" column="teacher_id"
javaType="com.hava.cloud_study.entity.Teacher">
<id property="id" column="teacherId" />
<result property="teacherName" column="teacherName" />
</association>
</collection>
</resultMap>
</mapper>
#MyBatis数据库连接池 ##DataSource
- MyBatis 3.0 内置数据库连接池
- dataSource type="POOLED"启动连接池
<!-- 数据库连接数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.1.200/cloud_study"/>
<property name="username" value="root"/>
<property name="password" value="dVHJtG0T:pf*"/>
</dataSource>
</environment>
##数据库连接生命周期 MyBatis数据库连接池的内部实现,与之前的DBCP连接池是非常类似的。MyBatis数据库连接池实际上维护了一个空闲连接链和活跃连接链两个队列,当MyBatis要实际执行SQL时,获取一个数据库连接,首先回去判断空闲的队列中是否有空闲的数据库连接,如果有返回数据库连接去执行SQL,如果没有,则判断当前活跃的数据库连接是否已经满,如果没有超过则创建新连接并加入到活跃列表中。如果已满或者超过则回去检查活跃连接队列中,最早的连接是否过期,如果过期则移除数据库连接。在创建新的数据库连接
##连接池常用配置选项
-
poolMaximumActiveConnections
-
- 数据库最大活跃连接数
-
- 考虑到随着连接数的增加,性能可能达到拐点,不建议设置过大。
-
poolMaximumIdleConnections
-
- 最大空闲连接数
-
- 经验值建议设置与poolMaximum相同即可
-
poolMaximumCheckoutTime
-
- 获取连接时如果没有idleConnection同时activeConnection达到最大值,则从activeConnections列表第一个连接开始,检查是否超过poolMaximumCheckoutTime,如果超过,则强制使其失效,返回该连接。
-
- 由于SQL执行时间受服务器配置、表结构不同,建议设置为预期最大SQL执行时间。
-
poolTimeToWait
-
- 获取服务器端数据库连接的超时时间,如果超过该时间,则打印日志,同时重新获取。
-
- 建议使用默认值20s
-
poolPingEnabled
-
- 启动连接侦测,检查连接池中的连接是否为有效连接
-
- 默认关闭,建议启动,防止服务器端异常关闭,导致客户端错误
-
poolPingQuery
-
- 侦测SQL,建议使用
select 1
开销较小
- 侦测SQL,建议使用
-
poolPingConnectionsNotUsedFor
-
- 侦测时间,建议小于服务器端超时时间,MySQL默认超时8小时
来源:oschina
链接:https://my.oschina.net/u/52678/blog/755829