1.概述
1.1.什么是框架
框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。
简而言之,框架其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统,或者说是使用别人搭好的舞台,你来做表演。框架一般是成熟的,不断升级的软件。
1.2.框架要解决的问题
框架要解决的最重要的一个问题是技术整合的问题,在JavaEE的 框架中,有着各种各样的技术,不同的软件企业需要从JavaEE中选择不同的技术,这就使得软件企业最终的应用依赖于这些技术,技术自身的复杂性和技术的风险性将会直接对应用造成冲击。而应用是软件企业的核心,是竞争力的关键所在,因此应该将应用自身的设计和具体的实现技术解耦。这样,软件企业的研发将集中在应用的设计上,而不是具体的技术实现,技术实现是应用的底层支撑,它不应该直接对应用产生影响。
1.3.软件开发的分层重要性
框架的重要性在于它实现了部分功能,并且能够很好的将低层应用平台和高层业务逻辑进行了缓和。为了实现软件工程中的“高内聚、低耦合”。把问题划分开来各个解决,易于控制,易于延展,易于分配资源。我们常见的MVC软件设计思想就是很好的分层思想。
通过分层更好的实现了各个部分的职责,在每一层将再细化出不同的框架,分别解决各层关注的问题。
1.4.分层开发下的常见框架
1.4.1 解决数据持久化问题的框架
Mybatis
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
作为持久层的框架,还有一个封装程度更高的框架就是Hibernate,但这个框架因为各种原因目前在国内的流行程度下降太多,现在公司开发也越来越少使用。目前Mybatis框架是主流,未来使用Spring Data来实现数据持久化也是一种趋势。
1.4.2 解决WEB层问题的框架
SpringMVC
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts2(一般老项目使用)等。
1.4.3 解决技术整合问题的框架
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
- 目的:解决企业应用开发的复杂性
- 功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
- 范围:任何Java应用
Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。
1.5 Mybatis框架概述
Mybatis是一个优秀的基于Java的持久层框架,它内部封装了Jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
Mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由Mybatis框架执行sql并将结果映射为java对象并返回。
采用ORM思想解决了实体和数据库映射的问题,对Jdbc进行了封装,屏蔽了Jdbc Api底层访问细节,使我们不用与Jdbc Api打交道,就可以完成对数据库的持久化操作。
ORM :操作对象既是操作数据库
为了我们能够更好掌握框架运行的内部过程,并且有更好的体验,下面我们将从自定义Mybatis框架开始来学习框架。此时我们将会体验框架从无到有的过程体验,也能够很好的综合前面阶段所学的基础。
2 .Mybatis 入门程序
案例场景: 根据 user 表,查询出所有“上海”的用户对象,返回一个pojo的 List集合。
2.1 初始化测试数据
1、创建数据库,数据库名:mybatisDemo
2、将如下Sql语句在数据库中执行,初始化测试数据
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
CREATE DATABASE mybatisdemo;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` date DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '张三', '2018-07-10', '1', '北京');
INSERT INTO `user` VALUES ('2', '李四', '2018-07-10', '1', '上海');
INSERT INTO `user` VALUES ('3', '王五', '2018-07-10', '1', '广州');
INSERT INTO `user` VALUES ('4', '王六', '2018-07-10', '1', '深圳');
INSERT INTO `user` VALUES ('5', '王八', '2018-07-10', '1', '上海');
2.2 JDBC 实现
代码:
创建Pojo 类
import java.util.Date;
public class User {
private Integer id;
private String username;
private String sex;
private Date birthday;
private String address;
// 省略getter ,setter,toString 方法
// ......
// ......
}
JDBC问题分析:
上边使用JDBC的原始方法(未经封装)实现了查询数据库表记录的操作。
-
数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
-
Sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变Java代码。
-
使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
-
对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。
下面我们将通过Mybatis 解决部分问题。
2.3 Mybatis 实现
步骤1-a: 创建Maven 工程,引入 Mybatis jar 包。(普通工程)
在pom 文件中加入一下代码
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
配置jdk编译环境
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
步骤1-b 创建Spring boot 工程, 引入以下依赖.(Spring Boot)
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
步骤2: 定义Dao接口和实现类
Dao接口
public interface UserDao {
User selectUserById(int id);
public List<User> selectUserByAddr(String address);
public class UserDaoImpl implements UserDao{
public User selectUserById(int id){
return null;
}
public List<User>selectUserByAddr(String address){
return null;
}
}
}
步骤3: 新建 mybatis-config.xml 文件(Spring Boot 请略过)
在resources 文件夹中新建一个 mybatis-config.xml 文件, 主要包含数据库连接信息
mybatis-config.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="env1">
<environment id="env1">
<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/mybatisdemo"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--
<mappers>
<mapper resource="mapper/UserDao.xml"/>
</mappers>
-->
</configuration>
步骤4: 创建映射文件
在resources 文件夹中新建一个 UserDao.xml 文件,主要是包含 SQL 语句
映射文件
<?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="net.suncaper.mybatisdemo.dao.UserDao">
<select id="selectUser1" parameterType="java.lang.String" resultType="net.suncaper.mybatisdemo.entity.User">
select id, username ,birthday,sex , address from user where address = #{address}
</select>
<select id="selectUser2" parameterType="_int" resultType="net.suncaper.mybatisdemo.entity.User">
select id, username ,birthday,sex , address from user where id = #{id}
</select>
</mapper>
注意: 需要把映射文件添加到配置文件中
步骤5: 实现查询User对象
UserDaoImpl.java
public class UserDaoImpl implements UserDao {
private SqlSession sqlSession;
//构造注入
public UserDaoImpl(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public User selectUserById(int id) {
return sqlSession.selectOne("s1",id);
}
@Override
public List<User> selectUserByAddr(String address) {
return sqlSession.selectList("s2",address);
}
}
Test运行测试代码
package demo2test;
import net.suncaper.dao.UserDao;
import net.suncaper.dao.UserDaoImpl;
import net.suncaper.entity.User;
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 org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MybatisTest {
SqlSession sqlSession = null;
@Before
public void before() {
try {
//1. 读取配置文件,(已经包含映射文件)
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 2. 创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3. 打开 sqlSession
sqlSession = sessionFactory.openSession();
} catch (IOException e) {
e.printStackTrace();
}
}
//想办法运行 映射文件的sql 语句
@Test
public void test0() {
User user = sqlSession.selectOne("s1", 1);
System.out.println(user);
System.out.println("==============================================================");
List<User> users = sqlSession.selectList("s2", "上海");
for (User u :
users) {
System.out.println(u);
}
}
@Test
public void test1() {
UserDao userDao = new UserDaoImpl(sqlSession);
System.out.println( userDao.selectUserById(1) );
}
@Test
public void test2() {
UserDao userDao = new UserDaoImpl(sqlSession);
List<User> users = userDao.selectUserByAddr("上海");
for (User u :
users) {
System.out.println(u);
}
}
}
==步骤6: 映射文件中添加 resultMap ==
<?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="net.suncaper.dao.UserDao">
<resultMap id="baseResultMap" type="net.suncaper.entity.User">
<id column="id" property="id"></id>
<result column="username" property="name"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</resultMap>
<select id="s1" parameterType="_int" resultMap="baseResultMap">
select id, username ,birthday,sex , address from user where id = #{value}
</select>
<select id="s2" parameterType="string" resultMap="baseResultMap">
select id, username ,birthday,sex , address from user where address = #{address}
</select>
</mapper>
步骤7: 改成用Mapper的方式来实现
- 写dao接口,习惯性用xxxMapper
写dao接口,习惯性用xxxMapper
public interface UserMapper {
User selectUserById(int id);
List<User> selectUserByAddr(String address);
}
- 保证映射文件中 sql 语句的id名和 mapper 接口中的方法名保持一致
mapper.xml 映射文件
<select id="selectUserById" parameterType="_int" resultMap="baseResultMap">
select id, username ,birthday,sex , address from user where id = #{value}
</select>
<select id="s2" parameterType="string" resultMap="baseResultMap">
select id, username ,birthday,sex , address from user where address = #{address}
</select>
- 直接使用 Mapper接口的代理类,(直接使用 Dao接口,Mybatis自动生产代理的实现类)
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectUserByAddr("上海");
2.4 Mybatis 使用 insert|update|delete|select 完成 CRUD
select:
- Mapper 接口方法
public Employee getEmployeeById(Integer id );
- Mapper 映射文件
<select id="getEmployeeById" resultType="net.suncaper.mybatis.beans.Employee">
select * from tbl_employee where id =${_parameter}
</select>
insert:
- Mapper 接口方法
public Integer insertEmployee(Employee employee);
- Mapper 映射文件
<insert id="insertEmployee" parameterType="com.mybatis.beans.Employee">
insert into tbl_employee(last_name,email,gender) values(#{lastName},#{email},#{gender})
</insert>
update:
- Mapper 接口方法
public Boolean updateEmployee(Employee employee);
- Mapper 映射文件
<update id="updateEmployee" >
update tbl_employee set last_name = #{lastName},email = #{email},gender = #{gender} where id = #{id}
</update>
delete:
- Mapper 接口方法
public void deleteEmployeeById(Integer id );
- Mapper 映射文件
<delete id="deleteEmployeeById" >
delete from tbl_employee where id= #{id}
</delete>
3.Mybatis案例
3.1(查询)
案例1:两个以上sql 参数
数据库:
drop database if exists mybatisdemo;
create database mybatisdemo;
use mybatisdemo;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` date DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
INSERT INTO `user` VALUES ('1', '张三', '2018-07-10', '1', '北京');
INSERT INTO `user` VALUES ('2', '李四', '2018-07-10', '0', '上海');
INSERT INTO `user` VALUES ('3', '王五', '2018-07-10', '1', '广州');
INSERT INTO `user` VALUES ('4', '王六', '2018-07-10', '1', '深圳');
INSERT INTO `user` VALUES ('5', '王八', '2018-07-10', '1', '上海');
create table employee(
id varchar(255) primary key,
name varchar(255)
)
场景: 查在上海的女人
select * from user whrere sex = ? and address = ?
- 通过Map 的方式
- Mapper 接口方法
List<User> selectUserByAddrAndSexA(Map<String,Object> map);
- Mapper 映射文件
<select id="selectUserByAddrAndSexA" parameterType="map" resultMap="userMap">
select * from user where sex = #{sex} and address = #{address}
</select>
- 测试代码
Map<String,Object> map = new HashMap<>();
map.put("sex",0);
map.put("address","上海");
List<User> userList = userMapper.selectUserByAddrAndSexA(map);
- 通过java Bean 的方式
- Mapper 接口方法
List<User> selectUserByAddrAndSexB(User user);
- Mapper 映射文件
<select id="selectUserByAddrAndSexB" parameterType="net.suncaper.mybatisdemo.domain.User" resultMap="userMap">
select * from user where sex = #{sex} and address = #{address}
</select>
- 测试代码
User user = new User();
user.setSex("0");
user.setAddress("上海");
List<User> userList = userMapper.selectUserByAddrAndSexB(user);
- 通过java Bean + 命名式参数 的方式
- Mapper 接口方法
List<User> selectUserByAddrAndSexC(@Param("u") User user);
- Mapper 映射文件
<select id="selectUserByAddrAndSexC" resultMap="userMap">
select * from user where sex = #{u.sex} and address = #{u.address}
</select>
- 测试代码
User user = new User();
user.setSex("0");
user.setAddress("上海");
List<User> userList = userMapper.selectUserByAddrAndSexC(user);
- 通过命名式参数 的方式
- Mapper 接口方法
List<User> selectUserByAddrAndSexD(@Param("a") String sex,@Param("b")String address);
- Mapper 映射文件
<select id="selectUserByAddrAndSexD" resultMap="userMap">
select * from user where sex = #{a} and address = #{b}
</select>
- 测试代码
List<User> userList = userMapper.selectUserByAddrAndSexD("0","上海");
案例2:以集合或数组作为参数
查询广州、北京、上海、深圳的人
- Mapper 接口方法
List<User> selectUserBySomeAddr(@Param("addresses") String[] addresses);
- Mapper 映射文件
<select id="selectUserBySomeAddr" resultMap="userMap">
select * from user where address in
<foreach collection="addresses" open="(" close=")" separator="," item="addr">
#{addr}
</foreach>
</select>
- 测试代码
List<User> userList= userMapper.selectUserBySomeAddr(new String[]{"上海","广州","深圳","北京"});
案例3:演示# 和 $ 的区别
- $ 只是字符串的拼接,不能防止SQL注入
- ${value} 会被直接替换,而 #{value} 会被使用 ?作为 预处理
- Mapper 接口方法
List<User> selectUserByName(String name);
- Mapper 映射文件
<select id="selectUserByName" parameterType="string" resultMap="userMap">
select * from user where username = '${value }'
</select>
- 测试代码
List<User> users = userMapper.selectUserByName("王八");
案例4:模糊查询
场景:查询名字 包含”王“ 字混球
方式1:通过$符号
- Mapper 接口方法
List<User> selectUserLikeName(String name);
- Mapper 映射文件
<select id="selectUserLikeName" parameterType="string" resultMap="userMap">
select * from user where username like '%${value}%'
</select>
- 测试代码
List<User> users = userMapper.selectUserLikeName("王");
方式2:通过字符串连接函数
- Mapper 接口方法
List<User> selectUserLikeName1(String name);
- Mapper 映射文件
<select id="selectUserLikeName1" parameterType="string" resultMap="userMap">
select * from user where username like CONCAT('%',#{value},'%')
</select>
- 测试代码
List<User> users = userMapper.selectUserLikeName1("王");
方式3:通过 标签
- Mapper 接口方法
List<User> selectUserLikeName2(@Param("name")String name);
- Mapper 映射文件
<select id="selectUserLikeName2" parameterType="string" resultMap="userMap">
<bind name="item" value="'%'+name+'%'"/>
select * from user where username like #{item}
</select>
- 测试代码
List<User> users = userMapper.selectUserLikeName2("王");
案例5:插入语句并获得主键
方式1: 通过useGeneratedKeys
通过 useGeneratedKeys拿到数据库自动增长的id值,赋给插入对象的主键字段(user 对象的id属性)
- Mapper 接口方法
void insertUser(User user);
- Mapper 映射文件
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex}, #{address} )
</insert>
- 测试代码
User user = new User("龙哥","0",new Date(),"重庆");
userMapper.insertUser(user);
//持有持久化ID
System.out.println(user.getId());
方式2: 通过数据库函数实现
自动增长的id : select last_insert_id()
获取uuid: select uuid()
- Mapper 接口方法
void insertEmployee(Employee employee);
void insertUser1(User user);
- Mapper 映射文件
<!-- 字符串类型作为主键 -->
<insert id="insertEmployee" parameterType="Employee">
<selectKey resultType="string" keyProperty="id" order="BEFORE">
select uuid()
</selectKey>
insert into employee(id,name) values(#{id},#{name})
</insert>
<!-- int 类型作为主键 -->
<insert id="insertUser1" parameterType="User">
<selectKey resultType="_int" keyProperty="id" order="AFTER">
select last_insert_id()
</selectKey>
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex}, #{address})
</insert>
- 测试代码
Employee employee = new Employee();
/* employee.setId(IDGener.getUUID()); 默认情况下,缺少主键不能插入数据库*/
employee.setName("员工222");
employeeMapper.insertEmployee(employee);
System.out.println(employee.getId());
//-------------------------------
User user = new User("黄爷","0",new Date(),"重庆");
userMapper.insertUser1(user);
System.out.println(user.getId());
案例6:更新和删除操作
通过删除和更新,事务处理
默认情况下 sqlSession 是事务不自动提交,需要显式提交
- Mapper 接口方法
public void deleteUserByName(User user);
void updateUserNameById(User user);
- Mapper 映射文件
<delete id="deleteUserByName" parameterType="User">
delete from user where username = #{username}
</delete>
<update id="updateUserNameById" parameterType="User">
update user set username = #{username} where id = #{id}
</update>
- 测试代码
@Test
public void testDeleteUserByName(){
boolean isAuto = false;
try {
isAuto = sqlSession.getConnection().getAutoCommit();
sqlSession.getConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
try {
User user = new User();
user.setUsername("李四");
userMapper.deleteUserByName(user);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
e.printStackTrace();
}
try {
sqlSession.getConnection().setAutoCommit(isAuto);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Test
public void testUpdateUserNameById(){
boolean isAuto = false;
try {
isAuto = sqlSession.getConnection().getAutoCommit();
sqlSession.getConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
try {
User user = new User();
user.setId(1);
user.setUsername("黄柯");
userMapper.updateUserNameById(user);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
e.printStackTrace();
}
try {
sqlSession.getConnection().setAutoCommit(isAuto);
} catch (SQLException e) {
e.printStackTrace();
}
}
3.2 Mybatis案例(返回结果)
数据库:
drop database if exists mybatisdemo;
create database mybatisdemo;
use mybatisdemo;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` date DEFAULT NULL COMMENT '生日',
`sex` char(1) DEFAULT NULL COMMENT '性别',
`address` varchar(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
INSERT INTO `user` VALUES ('1', '张三', '2018-07-10', '1', '北京');
INSERT INTO `user` VALUES ('2', '李四', '2018-07-10', '0', '上海');
INSERT INTO `user` VALUES ('3', '王五', '2018-07-10', '1', '广州');
INSERT INTO `user` VALUES ('4', '王六', '2018-07-10', '1', '深圳');
INSERT INTO `user` VALUES ('5', '王八', '2018-07-10', '1', '上海');
案例1:返回单一实体对象
- Mapper 接口方法
User selectUserById(int id);
- Mapper 映射文件
<select id="selectUserById" parameterType="_int" resultMap="userMap">
select * from `user` where id = #{id}
</select>
- 测试代码
User user = userMapper.selectUserById(2);
System.out.println(user);
案例2:返回集合实体对象
- Mapper 接口方法
List<User> selectAllUser();
- Mapper 映射文件
<select id="selectAllUser" resultMap="userMap">
select * from `user`
</select>
- 测试代码
List<User> users = userMapper.selectAllUser();
for (User user : users) {
System.out.println(user);
}
案例3:返回Map的实体对象集合
- Mapper 接口方法
@MapKey("id")
Map<Integer,User> selectAllMapUser();
- Mapper 映射文件
<select id="selectAllMapUser" resultMap="userMap">
select * from `user`
</select>
- 测试代码
Map<Integer, User> userMap = userMapper.selectAllMapUser();
//遍历Map
Set<Integer> keys = userMap.keySet();
for (Integer key : keys) {
System.out.println(key + " --> " +userMap.get(key));
}
案例4:返回单个对象的Map形式
- Mapper 接口方法
Map<String,Object> selectOneMapUser(int id);
- Mapper 映射文件
<select id="selectOneMapUser" resultType="HashMap">
select * from `user` where id= #{id}
</select>
- 测试代码
Map<String, Object> integerUserMap = userMapper.selectOneMapUser(1);
System.out.println(integerUserMap);
案例5:返回Map形式的数据行
- Mapper 接口方法
List< Map<String,Object> > selectAllMapCol();
- Mapper 映射文件
<select id="selectAllMapCol" resultType="map">
select * from `user`
</select>
- 测试代码
List<Map<String, Object>> maps = userMapper.selectAllMapCol();
for (Map<String, Object> map : maps) {
Set<String> cols = map.keySet();
for (String col:cols) {
System.out.print(col+ ":"+map.get(col) +"\t");
}
System.out.println();
}
4. 动态SQL
- 动态 SQL是MyBatis强大特性之一。极大的简化我们拼装SQL的操作
- 动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似
- MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作
- OGNL( Object Graph Navigation Language )对象图导航语言,这是一种强大的表达式语言,通过它可以非常方便的来操作对象属性。 类似于我们的EL,SpEL等
访问对象属性: person.name
调用方法: person.getName()
调用静态属性/方法: @java.lang.Math@PI @java.util.UUID@randomUUID()
调用构造方法: new com.atguigu.bean.Person(‘admin’).name
运算符: +,-*,/,%
逻辑运算符: in,not in,>,>=,<,<=,==,!=
注意:xml中特殊符号如”,>,<等这些都需要使用转义字符
4.1 if where
- If用于完成简单的判断.
- Where用于解决SQL语句中where关键字以及条件中第一个and或者or的问题
<select id="getEmpsByConditionIf" resultType="net.suncaper.mybatis.domain.Employee">
select id , last_name ,email , gender
from tbl_employee
<where>
<if test="id!=null">
and id = #{id}
</if>
<if test="lastName!=null && lastName!=""">
and last_name = #{lastName}
</if>
<if test="email!=null and email.trim()!=''">
and email = #{email}
</if>
<if test=""m".equals(gender) or "f".equals(gender)">
and gender = #{gender}
</if>
</where>
</select>
4.2 trim
- Trim 可以在条件判断完的SQL语句前后 添加或者去掉指定的字符
- prefix: 添加前缀
- prefixOverrides: 去掉前缀
- suffix: 添加后缀
- suffixOverrides: 去掉后缀
<select id="getEmpsByConditionTrim" resultType="net.suncaper.mybatis.domain.Employee">
select id , last_name ,email , gender
from tbl_employee
<trim prefix="where" suffixOverrides="and">
<if test="id!=null">
id = #{id} and
</if>
<if test="lastName!=null && lastName!=""">
last_name = #{lastName} and
</if>
<if test="email!=null and email.trim()!=''">
email = #{email} and
</if>
<if test=""m".equals(gender) or "f".equals(gender)">
gender = #{gender}
</if>
</trim>
</select>
4.3 set
- set 主要是用于解决修改操作中SQL语句中可能多出逗号的问题
<update id="updateEmpByConditionSet">
update tbl_employee
<set>
<if test="lastName!=null && lastName!=""">
last_name = #{lastName},
</if>
<if test="email!=null and email.trim()!=''">
email = #{email} ,
</if>
<if test=""m".equals(gender) or "f".equals(gender)">
gender = #{gender}
</if>
</set>
where id =#{id}
</update>
4.4 choose(when、otherwise)
- choose 主要是用于分支判断,类似于java中的switch case,只会满足所有分支中的一个
<select id="getEmpsByConditionChoose" resultType="net.suncaper.mybatis.domain.Employee">
select id ,last_name, email,gender from tbl_employee
<where>
<choose>
<when test="id!=null">
id = #{id}
</when>
<when test="lastName!=null">
last_name = #{lastName}
</when>
<when test="email!=null">
email = #{email}
</when>
<otherwise>
gender = 'm'
</otherwise>
</choose>
</where>
</select>
4.5 for each
- for each 主要用于循环迭代
-
collection: 要迭代的集合
-
item: 当前从集合中迭代出的元素
-
open: 开始字符
-
close:结束字符
-
separator: 元素与元素之间的分隔符
-
index:
-
迭代的是List集合: index表示的当前元素的下标
-
迭代的Map集合: index表示的当前元素的key
-
-
<select id="getEmpsByConditionForeach" resultType="net.suncaper.mybatis.domain.Employee">
select id , last_name, email ,gender from tbl_employee where id in
<foreach collection="ids" item="curr_id" open="(" close=")" separator="," >
#{curr_id}
</foreach>
</select>
4.6 sql
- sql 标签是用于抽取可重用的sql片段,将相同的,使用频繁的SQL片段抽取出来,单独定义,方便多次引用.
- 抽取SQL:
<sql id="selectSQL">
select id , last_name, email ,gender from tbl_employee
</sql>
- 引用SQL:
<include refid="selectSQL"></include>
5. 关联查询
5.1一对一关联
需要
根据班级id查询班级信息(带老师的信息)
创建表和数据
CREATE TABLE teacher(
t_id INT PRIMARY KEY AUTO_INCREMENT,
t_name VARCHAR(20)
);
CREATE TABLE class(
c_id INT PRIMARY KEY AUTO_INCREMENT,
c_name VARCHAR(20),
teacher_id INT
);
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);
定义实体类
public class Teacher {
private int id;
private String name;
}
public class Classes {
private int id;
private String name;
private Teacher teacher;
}
定义sql映射文件ClassMapper.xml
<select id="getClass" parameterType="int" resultMap="ClassResultMap">
select * from class c, teacher t where c.teacher_id=t.t_id and c.c_id=#{id}
</select>
<resultMap type="_Classes" id="ClassResultMap">
<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>
5.2一对多关联
需求
根据classId查询对应的班级信息,包括学生,老师
创建表和数据
CREATE TABLE student(
s_id INT PRIMARY KEY AUTO_INCREMENT,
s_name VARCHAR(20),
class_id INT
);
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);
定义实体类
public class Student {
private int id;
private String name;
}
public class Classes {
private int id;
private String name;
private Teacher teacher;
private List<Student> students;
}
定义sql映射文件ClassMapper.xml
<select id="getClass3" parameterType="int" resultMap="ClassResultMap3">
select * from class c, teacher t,student s where c.teacher_id=t.t_id and c.C_id=s.class_id and c.c_id=#{id}
</select>
<resultMap type="_Classes" id="ClassResultMap3">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" javaType="Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
<!-- ofType指定students集合中的对象类型 -->
<collection property="students" ofType="Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
6. Mybatis 缓存
-
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率
-
MyBatis系统中默认定义了两级缓存
一级缓存二级缓存
-
默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
-
二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
-
为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
6.1 一级缓存的使用
- 一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空。
- 本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域.
- 在1之后, 可以配置本地缓存的作用域. 在 mybatis.xml 中配置
- 一级缓存的工作机制
- 同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中
- key: hashCode+查询的SqlId+编写的sql查询语句+参数
6.2一级缓存失效的几种情况
- 不同的SqlSession对应不同的一级缓存
- 同一个SqlSession但是查询条件不同
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
6.3二级缓存的使用
- 二级缓存(second level cache),全局作用域缓存
- 二级缓存默认不开启,需要手动配置
- MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
- 二级缓存在 SqlSession 关闭或提交之后才会生效
- 二级缓存使用的步骤:
- 全局配置文件中开启二级缓存
- 需要使用二级缓存的映射文件处使用cache配置缓存
- 注意:POJO需要实现Serializable接口
- 二级缓存相关的属性
-
eviction=“FIFO”:缓存回收策略:
-
LRU – 最近最少使用的:移除最长时间不被使用的对象。
-
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
-
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
-
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。默认的是 LRU。
-
-
flushInterval:刷新间隔,单位毫秒。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
-
size:引用数目,正整数。代表缓存最多可以存储多少个对象,太大容易导致内存溢出
-
readOnly:只读,true/false
- true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
- false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
-
6.4 缓存的相关属性设置
-
全局setting的cacheEnable:
配置二级缓存的开关,一级缓存一直是打开的。 -
select标签的useCache属性:
配置这个select是否使用二级缓存。一级缓存一直是使用的 -
sql标签的flushCache属性:
增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。查询默认 flushCache=false。
-
clearCache():只是用来清除一级缓存。
6.5 整合第三方缓存
- 为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
- EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider
- 整合EhCache缓存的步骤:
-
导入ehcache包,以及整合包,日志包
ehcache-core-2.6.8.jar、mybatis-ehcache-1.0.3.jar -
slf4j-api-1.6.1.jar、slf4j-log4j12-1.6.2.jar
-
编写xml配置文件
-
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\atguigu\ehcache" />
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
<!--
属性说明:
l diskStore:指定数据在磁盘中的存储位置。
l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
以下属性是必须的:
l maxElementsInMemory - 在内存中缓存的element的最大数目
l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
-->
- 配置cache标签
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
7. Mybatis 代码反向生成器(MBG)
7.1、插件的使用
描述: mybatis generator 的作用是根据数据库自动生成 实体类、Dao接口、Mapper 映射文件。
数据库:
create table product(
id int primary key AUTO_INCREMENT,
pname varchar(20),
price double,
category_id varchar(32)
);
INSERT INTO product(pname,price,category_id) VALUES('联想',5000,'电脑');
INSERT INTO product(pname,price,category_id) VALUES('海尔',3000,'洗衣机');
INSERT INTO product(pname,price,category_id) VALUES('雷神',5000,'笔记本');
INSERT INTO product(pname,price,category_id) VALUES('JACK JONES',800,'服饰');
INSERT INTO product(pname,price,category_id) VALUES('真维斯',200,'服饰');
INSERT INTO product(pname,price,category_id) VALUES('花花公子',440,'服饰');
INSERT INTO product(pname,price,category_id) VALUES('劲霸',2000,'服饰');
INSERT INTO product(pname,price,category_id) VALUES('香奈儿',800,'奢侈品');
INSERT INTO product(pname,price,category_id) VALUES('相宜本草',200,'亲民品');
INSERT INTO product(pname,price,category_id) VALUES('面霸',5,'垃圾');
INSERT INTO product(pname,price,category_id) VALUES('好想你枣',56,'零食');
INSERT INTO product(pname,price,category_id) VALUES('香飘飘奶茶',1,'毒药');
INSERT INTO product(pname,price,category_id) VALUES('果9',1,NULL);
步骤1:在pom.xml 中添加MBG插件
- pom.xml 中添加以下代码
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<configurationFile>${basedir}/src/main/resources/mybatis/generator/generatorConfig.xml</configurationFile>
<!-- 设置覆盖原有的文件:第二次执行的时候 -->
<overwrite>true</overwrite>
<!-- 输出一些日志信息-->
<verbose>true</verbose>
</configuration>
<dependencies>
<!-- maven 插件使用依赖包要独立设置 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
</dependencies>
</plugin>
步骤2:编写 generatorConfig.xml 文件
- 在 src/main/resources/mybatis/generator目录下新建一个 generatorConfig.xml
- 设置实体类、Mapper 映射文件、Dao 接口对应位置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="MySQLTables" targetRuntime="MyBatis3">
<!-- 虽然插件中配置了<overwrite>true</overwrite>,但是只对java 文件有效,对mapper文件无效.特定使用 UnmergeableXmlMappersPlugin 才可以覆盖xml文件 -->
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
<!--关闭注释的生成-->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--覆盖生成XML文件-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/mybatisDemo"
userId="root"
password="root">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 实体类-->
<javaModelGenerator targetPackage="net.suncaper.demo1.domain" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--Mapper 映射文件-->
<sqlMapGenerator targetPackage="mybatis.mapper" targetProject="src/main/resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!--Mapper 到接口 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="net.suncaper.demo1.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 对应的表生成对应的 实体类 -->
<table tableName="product" domainObjectName="Product"/>
<!-- <table schema="DB2ADMIN" tableName="ALLTYPES" domainObjectName="Customer" >
<property name="useActualColumnNames" value="true"/>
<generatedKey column="ID" sqlStatement="DB2" identity="true" />
<columnOverride column="DATE_FIELD" property="startDate" />
<ignoreColumn column="FRED" />
<columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" />
</table>-->
</context>
</generatorConfiguration>
- Mapper 映射文件
<select id="selectAllUser" resultMap="userMap">
select * from `user`
</select>
- 测试代码
List<User> users = userMapper.selectAllUser();
for (User user : users) {
System.out.println(user);
}
步骤3:通过运行maven 插件对应的方法
- 打开Idea 中maven 的视图 , Plugins → mybatis-generator → mybatis-generator:generator
- 截图
8. PageHelper分页插件
链接: 官方文档.
8.1. 引入分页插件
在 pom.xml 中添加下面的依赖:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.3</version>
</dependency>
在MyBatis全局配置文件中配置分页插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
8.2.分页插件参数介绍
- helperDialect:分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect属性来指定分页插件使用哪种方言。配置时,可以使用下面的缩写值:
oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derby - offsetAsPageNum:默认值为 false,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为 true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分页。
- rowBoundsWithCount:默认值为false,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为true时,使用 RowBounds 分页会进行 count 查询。
- pageSizeZero:默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
- reasonable:分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
- params:为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero。
- supportMethodsArguments:支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest。
- autoRuntimeDialect:默认值为 false。设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页 (不支持自动选择sqlserver2012,只能使用sqlserver)。
- closeConn:默认值为 true。当使用运行时动态数据源或没有设置 helperDialect 属性自动获取数据库类型时,会自动获取一个数据库连接, 通过该属性来设置是否关闭获取的这个连接,默认true关闭,设置为 false 后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定。
8.3.Page对象的使用
在查询之前通过PageHelper.startPage(页码,条数)设置分页信息,紧跟在这条信息后面的查询语句即为分页查询。
Page<Object> objects = PageHelper.startPage(2, 5);
List<Product> products = mapper1.selectByExample(productExample);
4.PageInfo对象的使用
在查询完数据后,使用PageInfo对象封装查询结果,可以获取更详细的分页信息以及可以完成分页逻辑。
Page<Object> objects = PageHelper.startPage(2, 5);
List<Product> products = mapper1.selectByExample(productExample);
PageInfo pageInfo=new PageInfo(products);
5.PageInfo对象参数介绍
//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//由于startRow和endRow不常用,这里说个具体的用法
//可以在页面中"显示startRow到endRow 共size条数据"
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总记录数
private long total;
//总页数
private int pages;
//结果集
private List<T> list;
//前一页
private int prePage;
//下一页
private int nextPage;
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
//导航条上的第一页
private int navigateFirstPage;
//导航条上的最后一页
private int navigateLastPage;
9 .Spring集成Mybatis
9.1.创建数据库
CREATE TABLE s_user(
user_id INT AUTO_INCREMENT PRIMARY KEY,
user_name VARCHAR(30),
user_birthday DATE,
user_salary DOUBLE
)
INSERT INTO s_user VALUE(1,"张三","2019-02-13",800);
INSERT INTO s_user VALUE(2,"李四","2019-02-13",123);
INSERT INTO s_user VALUE(3,"王五","2019-02-13",998);
INSERT INTO s_user VALUE(4,"赵六","2019-02-13",2000);
9.2.mybatis逆向工程
generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="MySQLTables" targetRuntime="MyBatis3">
<!-- 虽然插件中配置了<overwrite>true</overwrite>,但是只对java 文件有效,对mapper文件无效.特定使用 UnmergeableXmlMappersPlugin 才可以覆盖xml文件 -->
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>
<!--关闭注释的生成-->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--覆盖生成XML文件-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/mybatisDemo"
userId="root"
password="root">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- 实体类-->
<javaModelGenerator targetPackage="net.suncaper.demo.domain" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!--Mapper 映射文件-->
<sqlMapGenerator targetPackage="mybatis.mapper" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!--Mapper 到接口 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="net.suncaper.demo.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 对应的表生成对应的 实体类 -->
<table tableName="s_user" domainObjectName="User"/>
</context>
</generatorConfiguration>
9.3.sping-mybatis配置文件
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"
xsi:schemaLocation="
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">
<!-- 1. 数据源 : DriverManagerDataSource -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisDemo"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--
2. 指定数据源和配置文件路径
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:conf/mybatis-config.xml"/>
<property name="mapperLocations">
<list>
<value>classpath:mybatis/mapper/*.xml</value>
</list>
</property>
</bean>
<!--
3. 扫描DAO接口所在包名,Spring会自动查找其下的类,并将其定义为一个Spring Bean
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="net.suncaper.demo.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
</beans>
9.4.mybatis配置文件
mybatis-config.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>
<!-- Spring整合myBatis后,这个配置文件基本可以不要了-->
<!-- 设置外部配置文件 -->
<!-- 设置类别名 -->
<typeAliases>
<package name="net.suncaper.demo.domain"/>
</typeAliases>
<!-- 设置数据库连接环境 -->
<!-- 映射文件 -->
</configuration>
9.5.编写测试类
Test.java
import net.suncaper.demo.domain.User;
import net.suncaper.demo.mapper.UserMapper;
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;
@RunWith(SpringJUnit4ClassRunner.class) //使用Springtest测试框架
@ContextConfiguration("/conf/spring-config.xml")
public class Test {
@Autowired //自动注入
private UserMapper userMapper;
@org.junit.Test
public void hh(){
User user = userMapper.selectByPrimaryKey(1);
System.out.println(user);
}
9.6.说明
Mybatis中的重要对象
SqlSession
SqlSession是用于执行持久化操作的对象,类似于JDBC中的Connection。它提供了面向数据库执行SQL命令所需的所有方法,可以通过SqlSession实例直接运行已映射的SQL语句。SqlSession对应着一次数据库回话。由于数据库会话不是永久的,因此SqlSession的生命周期也不应该是永久的。相反,在每次访问数据库时都需要创建它。需要注意的是,每个线程都有自己的SqlSession实例,SqlSession实例不能被共享,也不是线程安全的。因此最佳的作用域范围是request作用域或者方法体作用域内。
SqlSessionFactory
每个数据库对应一个 SqlSessionFactory。所以,如果你想连接两个数据库,你需要创建两个 SqlSessionFactory 实例。以此类推。
关于SqlSession,SqlSessionFactory,SqlSessionFactoryBean的关系。
Mybatis核心是获取 SqlSession 实例。要获得 SqlSession 实例,则需要依赖 SqlSessionFactory 实例,通过openSession()方法获得。而 SqlSessionFactory 是 SqlSessionFactoryBuilder 依据 MyBatis 配置文件中的数据源、Sql映射文件等信息来构建的。在 MyBatis 中,SqlSessionFactory 的实例需要使用 SqlSessionFactoryBuilder 创建;而在集成环境中,则可以使用 MyBatis-Spring 整合包中的 SqlSessionFactoryBean 来代替。SqlSessionFactoryBean 封装了使用 SqlSessionFactoryBuilder 创建 SqlSessionFactory 的过程,我们可以在 Spring 中以配置文件的形式,通过配置 SqlSessionFactoryBean 获得 SqlSessionFactory 实例。
@Autowired报错
解决办法是:降低Autowired检测的级别,将Severity的级别由之前的error改成warning或其它可以忽略的级别。
@RunWith(SpringJUnit4ClassRunner.class)
让测试运行于spring测试环境
@ContextConfiguration
用来指定加载的Spring配置文件的位置,会加载默认配置文件。
@ContextConfiguration 注解有以下两个常用的属性:locations:可以通过该属性手工指定 Spring 配置文件所在的位置,可以指定一个或多个 Spring 配置文件用,分开。如下所示: @ContextConfiguration(locations={“aa/aa.xml”,” aa/bb.xml”})
inheritLocations:是否要继承父测试用例类中的 Spring 配置文件,默认为 true。