Mybatis框架学习笔记
Mybatis介绍
Mybatis简单使用
-
初始化SqlSessionFactory对象
SqlSessionFactory对象需要从一个核心配置文件中构建,因此我们创建SqlSessionFactory对象之前需要先配置一个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"> <configuration> <!-- 引入外部properties文件,路径从类路径的根目录开始 --> <properties resource="jdbc.properties" /> <settings> <!-- 开启将数据库中下划线连接的字段自动映射为Java的小驼峰命名 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!-- 定义类型别名,在xxxMapper.xml文件中就可以用别名代替很长的类名 --> <typeAliases> <!-- 单个类配置别名 --> <!-- <typeAlias type="com.lanou3g.mybatis.bean.User" alias="User" />--> <!-- 统一配置某个包下所有类的别名, 会使用 Bean 的首字母小写的类名来作为它的别名。 --> <package name="com.lanou3g.mybatis.bean" /> </typeAliases> <!-- 配置不同环境的参数 --> <environments default="development"> <!-- 开发环境数据库、事务配置 --> <environment id="development"> <!-- 事务管理使用JDBC的事务 --> <transactionManager type="JDBC"/> <!-- 配置开发环境数据源 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> <!-- 将所有driver.开头的参数,附加到url属性的值后面上 --> <property name="driver.characterEncoding" value="utf8"/> </dataSource> </environment> </environments> <!-- 将mapper SQL映射文件包含进来 --> <mappers> <!-- 将通过XML方式配置的mapper引入进来 --> <mapper resource="mapper/userMapper.xml"/> <!-- 将通过注解方式配置的mapper引入进来 --> <!-- <mapper class="com.lanou3g.mybatis.mapper.UserMapper" />--> <!-- 将com.lanou3g.mybatis.mapper包下所有通过注解方式配置的mapper引入进来 --> <!-- <package name="com.lanou3g.mybatis.mapper"/>--> </mappers> </configuration>
构建对象
// 1. 初始化mybatis配置 String confPath = "mybatis_conf.xml"; InputStream in = Resources.getResourceAsStream(confPath); // 2. 构建SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
-
创建SqlSession对象
通过上一步的SqlSessionFactory对象可以获取到负责执行SQL语句的SqlSession对象
// 3. 获取SqlSession对象,默认事务不自动提交 // SqlSession sqlSession = sqlSessionFactory.openSession(); // 获取一个自动提交事务的sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(true);
-
用SqlSession对象从Mybatis中获取Mapper接口的实现类
// 4. 获取Mapper UserMapper mapper = sqlSession.getMapper(UserMapper.class);
-
编写Mapper对象的xml配置文件
XML格式的Mapper配置文件类似于接口的实现类,它指定了具体要执行的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="com.lanou3g.mybatis.mapper.UserMapper"> <select id="queryAllUser" resultType="user"> select * from user </select> <insert id="insertUser"> insert into user (username,nick_name,email) values (#{username},#{nickName},#{email}) </insert> </mapper>
深入了解Mybatis
主要组件
核心配置文件
核心配置文件是Mybatis的入口,它里面可以配置mybatis的具体参数、数据源、类型别名、关联映射文件等。。
具体的参数配置说明参见:
SqlSessionFactory
一个SqlSessionFactory只能连接一个数据库实例,如果需要连接多个数据库,需要构建多个SqlSessionFactory对象。
在构建SqlSesssionFactory时可以指定environment的id,表示使用指定的数据源来构建factory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in, "dev");
SqlSession
一个SqlSession对象代表一次到数据的会话,该对象有一下功能:
- 获取Mapper实现类
- 管理事务操作
注意: SqlSession对象是非线程安全的,在多线程环境下,建议不要作为类的实例属性来用。
Mapper
-
Mapper接口类
定义了增删盖查的方法。注意,必须是接口类型,而且方法只需要定义就可以了。
-
Mapper配置文件
Mapper配置文件中就是负责实现接口中的方法,它定义了具体要执行什么SQL语句,如何映射结果集。
- 配置文件中select、delete、update、insert标签的id必须是对应接口中的方法名。
- mapper文件的namespace属性需要对应Mapper接口的完全类型限定名。
深入Mybatis核心配置文件
具体的参数配置说明参见:
深入Mybatis映射配置文件
CRUD语句定义
查询语句
接口中
List<User> queryAllUser();
User queryUserById(Integer id);
xml配置文件中
<select id="queryAllUser" resultType="user">
select * from user
</select>
<select id="queryUserById" resultType="user">
select * from user where id = #{id}
</select>
插入语句
普通插入语句
接口中
int insertUser(User user);
xml配置文件中
<insert id="insertUser">
insert into user (username,nick_name,email)
values (#{username},#{nickName},#{email})
</insert>
如何返回数据库自增的ID
<!-- 给insert语句添加useGeneratedKeys、keyProperty后,mybatis会将自增的id值直接赋值到传进来的user对象的id属性上
useGeneratedKeys: 指定需要获取数据库自增的id
keyProperty: 指定自增地段的名称
-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into user (username,nick_name,email)
values (#{username},#{nickName},#{email})
</insert>
删除语句
接口中
void deleteUserById(Integer id);
XML配置中
<delete id="deleteUserById">
delete from user where id = #{id}
</delete>
更新语句
接口中
void updateUser(User user);
XML配置中
<update id="updateUser">
update user set password = #{password} where id = #{id}
</update>
接口中的参数如果传递到SQL中
简单类型参数
接口中:
void deleteUserById(Integer id);
xml配置文件中:
<delete id="deleteUserById">
delete from user where id = #{id}
</delete>
引用类型参数
接口中:
int insertUser(User user);
xml配置文件中:
<!-- 给insert语句添加useGeneratedKeys、keyProperty后,mybatis会将自增的id值直接赋值到传进来的user对象的id属性上
useGeneratedKeys: 指定需要获取数据库自增的id
keyProperty: 指定自增地段的名称
-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into user (username,nick_name,email)
values (#{username},#{nickName},#{email})
</insert>
当接口中参数和XML配置取值时名称不一样时
在接口中的参数前加注解
User queryUserById(@Param("id") Integer xxxxxxxId);
在XML中取值时用注解指定的名称
<select id="queryUserById" resultType="user">
select * from user where id = #{id}
</select>
#{}与${}的区别
- 它俩都可以获取接口调用中传递过来的参数
- #{}会将参数作为占位符,使用预编译语句(PreparedStatement)执行
- 会 直 接 用 实 际 参 数 替 换 {}会直接用实际参数替换 会直接用实际参数替换{}, 参数可以作为SQL的一部分。
比如,要实现下面的效果:
Java代码
List<User> queryAllUserOrderBy(String order);
// 调用:
mapper.queryAllUserOrderBy("last_login_time");
XML配置:
<select id="queryAllUserOrderBy" resultMap="userMap">
select * from user order by ${order} desc;
</select>
如果上面使用#{order}取获取参数,最终执行的SQL会是这样:
==> Preparing: select * from user order by ? desc;
==> Parameters: last_login_time(String)
无法实现排序效果
如果使用${order}来取参数,最终执行SQL:
==> Preparing: select * from user order by last_login_time desc;
==> Parameters:
可以实现排序效果
结果集如何映射
ResultType方式
ResultType方式适用于数据库结果集可以直接映射成一个Java类的情况
Java实体类:
@Getter
@Setter
@ToString
public class User {
private Integer id;
private String username;
private String nickName;
private String password;
private String email;
private Timestamp lastLoginTime;
}
使用方法:
<select id="queryAllUser" resultType="com.lanou3g.bean.User">
select * from user
</select>
ResultMap方式
ResultMap方式适用于复杂的结果集映射,比如数据库返回的结果集中的列名和JavaBean无法一一对应,或者对象间存在一对一、一对多关联映射时。
解决数据库列名与Java类中属性名不一致的映射问题
<mapper>
...
<resultMap id="userMap" type="user">
<id property="id" column="id" />
<result property="username" column="username" />
<result property="lastLoginttime" column="last_login_time" />
<result property="email" column="email" />
<result property="nickName" column="nick_name" />
</resultMap>
...
</mapper>
在查询语句中将resultType换成resultMap
<select id="queryAllUser" resultMap="userMap">
select * from user
</select>
其实,如果遇到单纯字段名和属性名不对应的情况,使用别名的方式更简单
解决一对一映射查询问题
创建实体类
package com.lanou3g.bean;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class Girl {
private Integer id;
private Integer isQueen;
private Integer kId;
private String name;
}
package com.lanou3g.mybatis.bean;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
@Getter
@Setter
@ToString
public class King {
private Integer id;
private String name;
private Queen queen;
private List<Girl> girls;
}
package com.lanou3g.mybatis.bean;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.io.Serializable;
@Getter
@Setter
@ToString
public class Queen {
private Integer id;
private Integer kId;
private String name;
}
创建接口
package com.lanou3g.mapping;
import com.lanou3g.bean.King;
public interface KingMapper {
King findKingById(Integer id);
}
配置核心配置文件(mybatis-confg.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>
<properties resource="jdbc.properties"></properties>
<typeAliases>
<package name="com.lanou3g.bean"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/KingMapper.xml"></mapper>
</mappers>
</configuration>
配置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.lanou3g.mybatis.mapper.KingMapper">
<!--第一种方式-->
<resultMap id="kingMap" type="king">
<id column="kid" property="id" />
<result column="kname" property="name" />
<!-- 一对一映射关系 -->
<association property="queen" javaType="queen" resultMap="queenMap" columnPrefix="queen_"/>
</resultMap>
<!--另外一种方式-->
<!--<resultMap id="kingMap" type="king">
<id column="kid" property="id" />
<result column="kname" property="name" />
<association property="queen" javaType="queen">
<id column="qid" property="id" />
<result column="qname" property="name" />
<result column="kid" property="kId" />
</association>
</resultMap>-->
<resultMap id="queenMap" type="queen">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="king_id" property="kId" />
</resultMap>
<!--sql查询语句根据实际情况书写-->
<select id="findKingById" resultMap="kingMap">
SELECT
k.id kid,
k.`name` kname,
g. NAME qname,
g.id gid,
g.k_id gkid,
g.is_queen is_queen
FROM
king k
LEFT JOIN girl g ON g.k_id = k.id
WHERE
g.is_queen = 1
AND k.id = #{id};
</select>
</mapper>
解决一对多映射查询问题
创建实体类、创建接口、配置核心配置文件与上面相同,与一对一映射查询不同的是配置mapper文件,
- 在一对一映射查询中是用标签,里面指定返回类型用javaType=“具体的类型(最好用全类名)”
- 在一对多映射查询中用标签,里面指定返回类型用ofType=“具体的类型(最好用全类名)”
<?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.lanou3g.mybatis.mapper.KingMapper">
<resultMap id="kingMap" type="king">
<id column="kid" property="id" />
<result column="kname" property="name" />
<!--两种方式的比较-->
<!-- 一对一映射关系 -->
<!--<association property="queen" javaType="queen" resultMap="queenMap" columnPrefix="queen_"/>-->
<!-- 一对多映射关系 -->
<collection property="girls" ofType="girl" resultMap="girlMap" columnPrefix="girl_" />
</resultMap>
<resultMap id="queenMap" type="queen">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="king_id" property="kId" />
</resultMap>
<resultMap id="girlMap" type="girl">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="king_id" property="kId" />
</resultMap>
<!--sql查询语句根据实际情况书写-->
<select id="findKingById" resultMap="kingMap">
SELECT
k.id kid,
k.`name` kname,
q.id queen_id,
q.`name` queen_name,
k.id queen_king_id,
g.id girl_id,
g.`name` girl_name,
k.id girl_king_id
FROM
king k
LEFT JOIN queen q ON k.id = q.k_id
LEFT JOIN girl g ON g.k_id = k.id
WHERE
k.id = 27
</select>
</mapper>
- property 映射到列结果的字段或属性 (实体类中的名字)
- column 数据库中的列名,或者是列的别名
- javaType 一个 Java 类的完全限定名,或一个类型别名
- association 一对一映射查询时使用的标签
- collection 一对多映射查询时使用的标签
- columnPrefix 指定 columnPrefix 列名前缀,允许你将带有这些前缀的 列映射到一个外部的结果映射中
- result 注入到字段或 JavaBean 属性的普通结果
- id 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
动态SQL
条件分支SQL
-
if
if包裹的内容,如果没有传入值,那么不会拼接到sql语句后面执行
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
在上面的代码中,如果没有传入“title”,那么所有处于“ACTIVE”状态的BLOG都会返回;反之若传入了“title”,那么就会对“title”一列进行模糊查找并返回 BLOG 结果
-
choose&when&otherwise
类似于java代码中的choose…case…default;
- 在标签内的内容,输入的条件符合哪个标签内的条件就执行哪一个标签内的内容,如果都不符合就执行标签内的内容
- 一旦有输入的条件有符合标签内的内容的,就执行这个标签内的内容,其他的和标签内的内容不再执行
<select id="queryAllUserByCondition" resultType="user">
SELECT * from `user`
<choose>
<when test="nickName != null">
nick_name like #{nickName}
</when>
<when test="status != null">
and `status` = #{status};
</when>
<otherwise>
and 1 = 1
</otherwise>
</choose>
</select>
循环SQL
- forEach
- 允许指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量
- 允许指定开头与结尾的字符串以及在迭代结果之间放置分隔符
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<!--
collection 当前不要遍历的对象
separator 遍历完一次后,在末尾添加的字符等。
open 遍历的sql以什么开头
close 遍历的sql以什么结尾
-->
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
- 可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。
- 当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。
- 当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
其他特殊SQL
-
where
- where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。
- 如果输出后语句的开头为“AND”或“OR”,where 元素也会将它们去除。
如下所示:
<select id="dynamicWhereTest" parameterType="Blog" resultType="Blog"> select * from t_blog <where> <if test="title != null"> title = #{title} </if> <if test="content != null"> and content = #{content} </if> <if test="owner != null"> and owner = #{owner} </if> </where> </select>
像上述例子中,如果 title=null, 而 content != null,那么输出的整个语句会是 select * from t_blog where content = #{content},而不是 select * from t_blog where and content = #{content},因为 MyBatis 会自动地把首个 and / or 给忽略。
-
set
set 元素可以用于动态包含需要更新的列,而舍去其它的
<update id="updateAuthorIfNecessary"> update Author <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set> where id=#{id} </update>
set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号 , 因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号
-
trim
- 去除sql语句中多余的and关键字,逗号
- 给sql语句前拼接 “where“、“set“以及“values(“ 等前缀,或者添加“)“等后缀,
- 可用于选择性插入、更新、删除或者条件查询等操作。
<trim prefix="WHERE" prefixOverrides="AND |OR "> </trim>
属性 描述 prefix 给sql语句拼接的前缀 suffix 给sql语句拼接的后缀 prefixOverrides 去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND" suffixOverrides 去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定
缓存
一级缓存(本地缓存)
什么是一级缓存?
Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。
也就是在同一个SqlSession中,执行相同的查询SQL,第一次会去数据库进行查询,并写到缓存中;
第二次以后是直接去缓存中取。
当执行SQL查询中间发生了增删改的操作,MyBatis会把SqlSession的缓存清空。
如何使用?
1 我们在一个 sqlSession 中,对 User 表根据id进行两次查询,查看他们发出sql语句的情况
@Test
public void testSelectOrderAndUserByOrderId(){
//根据 sqlSessionFactory 产生 session
SqlSession sqlSession = sessionFactory.openSession();
String statement = "one.to.one.mapper.OrdersMapper.selectOrderAndUserByOrderID";
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//第一次查询,发出sql语句,并将查询的结果放入缓存中
User u1 = userMapper.selectUserByUserId(1);
System.out.println(u1);
//第二次查询,由于是同一个sqlSession,会在缓存中查找查询结果
//如果有,则直接从缓存中取出来,不和数据库进行交互
User u2 = userMapper.selectUserByUserId(1);
System.out.println(u2);
sqlSession.close();
}
2 同样是对user表进行两次查询,只不过两次查询之间进行了一次update操作。
@Test
public void testSelectOrderAndUserByOrderId(){
//根据 sqlSessionFactory 产生 session
SqlSession sqlSession = sessionFactory.openSession();
String statement = "one.to.one.mapper.OrdersMapper.selectOrderAndUserByOrderID";
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//第一次查询,发出sql语句,并将查询的结果放入缓存中
User u1 = userMapper.selectUserByUserId(1);
System.out.println(u1);
//第二步进行了一次更新操作,sqlSession.commit()
u1.setSex("女");
userMapper.updateUserByUserId(u1);
sqlSession.commit();
//第二次查询,由于是同一个sqlSession.commit(),会清空缓存信息
//则此次查询也会发出 sql 语句
User u2 = userMapper.selectUserByUserId(1);
System.out.println(u2);
sqlSession.close();
}
总结:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bbVY76kP-1592645759790)(https://images2017.cnblogs.com/blog/1120165/201708/1120165-20170810230553636-190248238.png)]
二级缓存
什么是二级缓存?
Mybatis的二级缓存是指mapper映射文件。二级缓存的作用域是同一个namespace下的mapper映射文件内容,多个SqlSession共享。Mybatis需要手动设置启动二级缓存。
二级缓存的开启与关闭
1.在 SqlMapConfig.xml 文件开启二级缓存
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
2.配置相关的 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.itheima.dao.IUserDao">
<!-- 开启二级缓存的支持 -->
<cache></cache>
</mapper>
标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。
3.配置 statement 上面的 useCache 属性
<!-- 根据 id 查询 -->
<select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>
将 UserDao.xml 映射文件中的标签中设置 useCache=”true”代表当前这个 statement 要使用 二级缓存,如果不使用二级缓存可以设置为 false。
注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。
二级缓存注意事项
当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化 方式来保存对象。
public class User implements Serializable { }
基于注解的二级缓存
- 在 SqlMapConfig 中开启二级缓存支持
<!-- 配置二级缓存 -->
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
2.在持久层接口中使用注解配置二级缓存
@CacheNamespace(blocking=true)
//mybatis 基于注解方式实现配置二级缓存
public interface IUserDao {}
Mybatis逆向工程
什么是Mybatis逆向工程?
可以针对单表自动生成MyBatis执行所需要的代码,包括:Mapper.java,Mapper.xml,实体类。
Mybatis逆向工程有什么不足?
逆向工程有它自身的局限性,逆向工程方法只能执行一次,如果再次执行就会重复生成对应的DAO接口,实体 类等资源。如果需要对表结构进行修改,那么就必须删除已经生成的所有资源,重新生成一次。
如何使用Mybatis逆向工程?
1.添加依赖jar包到pom.xml文件。
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.southwind</groupId>
<artifactId>MBGMaven</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>MBGMaven Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
</dependency>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<build>
<finalName>MBGMaven</finalName>
</build>
</project>
2.创建MBG配置文件generatorConfig.xml:
(1) jdbcConnection配置数据库连接信息。
(2) javaModelGenerator配置javaBean的生成策略。
(3) sqlMapGenerator 配置sql映射文件生成策略。
(4) javaClientGenerator配置Mapper接口的生成策略。
(5) table配置要逆向解析的数据表(tableName:表名,domainObjectName:对应的javaBean名)。
<?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="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 删除自动生成注释 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 配置数据库连接信息 -->
<jdbcConnection
driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8"
userId="root"
password="root">
</jdbcConnection>
<!-- 数据类型解析,false表示将DECIMAL和 NUMERIC类型解析为 Integer,true表示解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 自动生成实体类存放的位置 -->
<javaModelGenerator targetPackage="com.southwind.entity"
targetProject="./src/main/java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- 自动生成Mapper.xml存放的位置 -->
<sqlMapGenerator targetPackage="com.southwind.dao"
targetProject="./src/main/resources">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- 自动生成Mapper接口存放的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.southwind.dao"
targetProject="./src/main/java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据库表 -->
<table tableName="t_user" domainObjectName="User"></table>
</context>
</generatorConfiguration>
3.创建GeneratorMain类,执行自动生成资源的代码。
package com.southwind.main;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;
public class GeneratorMain {
public static void main(String[] args) throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
String genCfg = "/generatorConfig.xml";
File configFile = new File(GeneratorMain.class.getResource(genCfg).getFile());
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = null;
try {
config = cp.parseConfiguration(configFile);
} catch (IOException e) {
e.printStackTrace();
} catch (XMLParserException e) {
e.printStackTrace();
}
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = null;
try {
myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
} catch (InvalidConfigurationException e) {
e.printStackTrace();
}
try {
myBatisGenerator.generate(null);
} catch (SQLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
批量插入
实体类
@Getter
@Setter
@ToString
public class User {
private Integer id;
private String nickName;
private Integer status;
private Timestamp createtime;
public User(Integer id, String nickName, Integer status, Timestamp createtime) {
this.id = id;
this.nickName = nickName;
this.status = status;
this.createtime = createtime;
}
public User() {
}
}
接口
public interface UserMapper2 {
List<User> queryAllUserByCondition(User condition);
List<User> queryUserByIn(List<Integer> ids);
int batchInsertByDynamicSql(List<User> userList);
int insertUser(User user);
}
配置文件
<!-- foreach的使用场合之 —— 批量插入 -->
<insert id="batchInsertByDynamicSql">
insert into user (nick_name, status, createtime)
values
<foreach collection="list" item="user" separator="," close=";">
(#{user.nickName},#{user.status},#{user.createtime})
</foreach>
</insert>
<insert id="insertUser">
insert into user (nick_name, status, createtime)
values
(#{nickName},#{status},#{createtime})
</insert>
方式一:foreach
private static void testBatchInsert(UserMapper2 mapper) {
List<User> userList = new ArrayList<>();
userList.add(new User(null, "王鼎", 1, new Timestamp(System.currentTimeMillis())));
userList.add(new User(null, "康健", 1, new Timestamp(System.currentTimeMillis())));
userList.add(new User(null, "冯翔宇", 1, new Timestamp(System.currentTimeMillis())));
userList.add(new User(null, "李世军", 1, new Timestamp(System.currentTimeMillis())));
userList.add(new User(null, "梁小", 1, new Timestamp(System.currentTimeMillis())));
int rows = mapper.batchInsertByDynamicSql(userList);
System.out.println("批量插入成功, rows: " + rows);
}
就是通过foreach来拼接sql语句,但不能无限拼接
方式二:ExecutorType.BATCH
private static void testBatchInsertByExecutorBatch(SqlSessionFactory sqlSessionFactory, UserMapper2 mapper) {
// 获取一个自动提交事务、批量执行的sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, true);
List<User> userList = new ArrayList<>();
for(int i = 100; i < 200; i++) {
userList.add(new User(null, "王鼎"+i, 1,
new Timestamp(System.currentTimeMillis())));
userList.add(new User(null, "康健"+i, 1,
new Timestamp(System.currentTimeMillis())));
userList.add(new User(null, "冯翔宇"+i, 1,
new Timestamp(System.currentTimeMillis())));
userList.add(new User(null, "李世军"+i, 1,
new Timestamp(System.currentTimeMillis())));
userList.add(new User(null, "梁小"+i, 1,
new Timestamp(System.currentTimeMillis())));
}
//设置每次执行的sql语句数量
int batchSize = 100;
int count = 0;
List<BatchResult> resultList = new ArrayList<>();
for(User user : userList) {
// ExecutorType.Batch方式这里返回的不是影响的条数,具体获取方法参见下面代码
mapper.insertUser(user);
count++;
if(count % batchSize == 0) {
//刷新
resultList.addAll(sqlSession.flushStatements());
}
}
if(count % batchSize != 0) {
resultList.addAll(sqlSession.flushStatements());
}
int rows = 0;
for(BatchResult batchResult : resultList) {
int[] updateCounts = batchResult.getUpdateCounts();
for(int updateCount : updateCounts) {
rows += updateCount;
}
}
System.out.println("批量插入成功,响应的行数:" + rows);
}
通过此方法来每次执行固定数量的sql语句
Spring与Mybatis整合
加入依赖
<!-- mybatis-spring整合依赖,这个是最主要的一个依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.1</version>
</dependency>
<!-- Spring和数据源相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
在Spring bean配置文件中配置Mybatis、Spring整合bean SqlSessionFactoryBean
<!-- 配置整合bean -->
<bean id="sessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据源是必要参数 -->
<property name="dataSource" ref="dataSource" />
<!-- Mybatis核心配置文件其实大多数情况下都可以省略,通过指定属性可以间接设置核心配置文件中的参数 -->
<!--<property name="configLocation" value="mybatis_conf.xml" />-->
<!-- 省略mybatis核心配置文件后,可以通过类似下面这些特定属性,设置mybatis参数 -->
<property name="typeAliasesPackage" value="com.lanou3g.mybatis.spring.bean" />
<property name="mapperLocations" value="classpath:mapper/*.xml" />
</bean>
在Spring bean配置文件中开启Mybatis Mapper扫描
-
需要使用mybatis schema
配置方法,在bean配置文件的头部添加
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://mybatis.org/schema/mybatis-spring
http://mybatis.org/schema/mybatis-spring.xsd">
-
开启Mybatis Mapper扫描
开启Mybatis Mapper扫描的作用是:告诉Mybatis要创建哪个包下接口的实现类,并以bean的方式加入到SpringIOC容器中
<!-- 开启Mapper扫描,Mybatis会创建将此包下的接口的实现类,并以bean的方式加入到SpringIOC容器中 -->
<mybatis:scan base-package="com.lanou3g.mybatis.spring.mapper" />
创建实体类、mapper映射文件、Mapper接口(可以通过Mybatis逆向工程直接生成)
Mapper接口
@Repository //此注解不是必须的,因为MessageMapper类的实现类是由Mybatis创建并放到ioc容器中的,不是由Spring来创建的。
public interface MessageMapper {
int insert(Message record);
List<Message> selectAll();
}
将Mapper接口用Spring自动注入的方式注入到需要的地方使用
MessageService.java
@Service
public class MessageService {
@Autowired
MessageMapper messageMapper;
public List<Message> queryAllMessage() {
return messageMapper.selectAll();
}
}
SpringMVC
MVC设计模式
其实在之前JavaWeb阶段我们已经接触到了MVC模式。
MVC里面的M指的的Model(通常包含bean、dao(mapper)、service);V指的是View,视图层,视图层主要的技术(JSP、HTML、FreeMaker、Themeleaf);C指的是Controller,控制层。控制层不负责具体数据、逻辑的处理和运算,它只负责将Model层的结果返回给对应的视图层去展示。
在JavaWeb阶段, Controller层指的就是Servlet; View层指的就是JSP或者HTML; Model层指的就是bean、dao、service。
在J2EE阶段,Controller层指的就是SpringMVC、Structs1\2; View层不变还是主流的页面展示技术; Model层包括bean、mybatis、service。
SpringMVC的优势
- SpringMVC是一款很轻量级的框架,要使用它的组件我们往往只需要定义一些最简单的Java类,然后添加某些注解就可以了
- SpringMVC的参数注入只直接注入到方法中,可以很好的做到不同请求间数据的隔离,而Struts2是注入到类实例变量上,不同的请求可能会覆盖参数。
- SpringMVC可以很轻易的和Spring整合,而Struts需要做比较复杂的配置。
SpringMVC简单使用
-
添加依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.0.RELEASE</version> </dependency>
-
在web.xml中配置DispatcherServlet
<servlet> <servlet-name>aa</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 指定SpringMVC 配置文件位置,DispatcherServlet初始化时会初始化Spring上下文(WebApplicationContext) --> <!-- 默认配置文件寻找位置:/WEB-INF/{servlet-name}-servlet.xml,如果名字符合默认寻找规则,可以不指定配置文件路径 --> <!--<init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/aa-servlet.xml</param-value> </init-param>--> <!-- 配置容器启动时初始化DispatcherServlet --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>aa</servlet-name> <!-- 映射路径配置成/,代表当前Servlet是一个默认Servlet,就是当其他Servlet都无法处理请求时,由默认Servlet出马 --> <url-pattern>/</url-pattern> <!-- <url-pattern>/*</url-pattern>--> </servlet-mapping>
-
配置SpringMVC dispatcher-servlet.xml
SpringMVC大部分组件都有默认配置,我们一般简单应用只需要指定视图解析器就行了
dispatcher-servlet.xml
<!-- 配置视图解析器,用于将Handler方法中返回的视图名解析成真正可展示的页面 --> <mvc:view-resolvers> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/" /> <property name="suffix" value=".jsp" /> </bean> </mvc:view-resolvers>
-
定义Controller
新建一个普通类,然后添加
@Controller
注解,就可以了 -
定义请求处理方法(Handler)
在Controller类中定义一个普通的方法,添加
@RequestMapping
注解就可以了
SpringMVC主要组件
RootApplicationContext和ServletApplicationContext
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Aid0J0mv-1592645759796)(https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/images/mvc-context-hierarchy.png)]
视图解析器
视图解析器的作用是将请求处理方法中的返回值解析成一个真正可以渲染的页面。
常用的视图解析器
-
InternalResourceViewResolver
内部资源解析器: 用于将返回值对应到项目路径下的某个可显示的页面。比如方法返回值是index字符串,那么
InternalResourceViewResolver
解析器会在index前加上指定的前缀,在index后加上指定的后缀来拼接成指向某个视图的路径。<0!-- 配置视图解析器,用于将Handler方法中返回的视图名解析成真正可展示的页面 --> <mvc:view-resolvers> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/" /> <property name="suffix" value=".jsp" /> </bean> </mvc:view-resolvers>
参数传递
页面参数传递到Controller的五种方法
1.直接将请求参数名作为Controller中方法的形参
public String login (String username,String password)
也就是直接把前台的页面中你想要获取过来的那个对应的name给当做参数传进来,Spring会自动去寻找
2.使用@RequestParam 绑定请求参数参数值(推荐使用)
public String login(RequestParam (“username”) String name,Model model)
这中使用@RequestParam 注解的话,括号里面引号引主的话,是需要和前台你想绑定的那个参数名保持一致的,而String name 这个参数你可以随便起名
3.用注解@RequestMapping接收参数的方法(推荐使用)
@RequestMapping(value=”/login/{username}/{password}”) public String login(@PathVariable(“username”) String name,@PathVariable(“password”) String pwd)
上面的 @RequestMapping(value=”/login/{username}/{password}”) 是以注解的方式写在方法上的。注解上的usernname和 password 必须和页面上value 相同,上面花括号{} 扩主的就相当于是一个占位符
而且这个和@requestParam不同之处在于,这个是从前台的静态界面中去拿到你想要的参数名并去到相对应的value值
但是使用@PathVariable这个注解,他是从请求路径中去拿到你想要的参数
4.使用Pojo对象(就是封装的类,类中封装的字段作为参数)绑定请求参数值
原理是利用Set的页面反射机制找到User对象中的属性
举例:
@ReauestMapping(value=/login”) public String login(User user){}
就是把封装的一个类当成一个参数放在方法中,封装类中的属性就是就是参数。用的时候通过这个POJO类型的实体可以去拿到他的所有的属性
5.使用原生的Servlet API 作为Controller 方法的参数
public String login(HttpServletRequest request) { String usernma=Request.getParameter(“username”); }
@RequestParam
-
作用:把请求中的指定名称的参数传递给控制器中的形参赋值
-
属性
2.1value:请求参数中的名称
2.2required:请求参数中是否必须提供此参数,默认值是true,必须提供equestHeader
3.代码视图
/**
*
接收请求
* @return
*/
@RequestMappi ng(path="/hello" )
public String sayHello(@RequestParam( value="username" , required=false)String name) {
System. out . println( "aa");
System. out . println(name);
return "sucess";
}
@RequestAttribute
1.作用:获取HTTP的请求(request)对象属性值,用来传递给控制器的参数。
2.属性
2.1
3.代码视图
3.1 创建Hello控制器
package com.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class Hello {
@RequestMapping("/show")
public String show(@RequestAttribute("name")String userName) {
System.out.println("userName="+userName);
return "success";
}
}
3.2创建index.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>index</title>
</head>
<body>
<%
request.setAttribute("name", "baixue");
request.getRequestDispatcher("/show").forward(request,response);
%>
</body>
</html>
@SessionAttribute
1.作用:用于多次执行控制器方法间的参数共享
2.属性
2.1value:指定存入属性的名称
3.代码视图
@Controller
@RequestMapping(path=" /user")
@Sessi onAttributes(value= { "username" ,”password" , "age"}, types=
{String. class , Integer.class})
//把数据存入到session域对象中
public class HelloController {
/**
*向session中存入值
*
@return
*/
@RequestMapping(path="/ save")
public String save(Model model) {
System . out . println("向session域中保存数据" );
model . addAttribute(" username", "root") ;
model . addAttribute(" password", "123");
model . addAttribute("age", 20);
return "success";
}
/**
*从session中获取值
* @return
*/
@RequestMapping(path=" / find")
public String find(ModelMap modelMap) {
String username = (String) modelMap. get(" username");
String password = (String) modelMap. get(" password");
Integer age = (Integer) modelMap. get("age ");
System. out . println(username +”: "+password +”: "+age);
return "success";
}
/**
*清除值
* @return
*/
@RequestMapping(path=" /delete" )
public String delete(SessionStatus status) {
@CookieValue
1.作用:用于获取指定cookie的名称的值
2.属性
2.1value: cookie的名称
3.代码视图
@RequestMapping(path=" /he1lo" )
public String sayHello(@CookieValue (value= "JSESSIONID") String cookieValue) {
System. out . println( cookieValue) ;
return "success" ;
}
Controller中的数据传递到页面
Model
ModelAndView
视图控制器
视图控制器的作用
ml>
##### @SessionAttribute
1.作用:用于多次执行控制器方法间的参数共享
2.属性
2.1value:指定存入属性的名称
3.代码视图
```Java
@Controller
@RequestMapping(path=" /user")
@Sessi onAttributes(value= { "username" ,”password" , "age"}, types=
{String. class , Integer.class})
//把数据存入到session域对象中
public class HelloController {
/**
*向session中存入值
*
@return
*/
@RequestMapping(path="/ save")
public String save(Model model) {
System . out . println("向session域中保存数据" );
model . addAttribute(" username", "root") ;
model . addAttribute(" password", "123");
model . addAttribute("age", 20);
return "success";
}
/**
*从session中获取值
* @return
*/
@RequestMapping(path=" / find")
public String find(ModelMap modelMap) {
String username = (String) modelMap. get(" username");
String password = (String) modelMap. get(" password");
Integer age = (Integer) modelMap. get("age ");
System. out . println(username +”: "+password +”: "+age);
return "success";
}
/**
*清除值
* @return
*/
@RequestMapping(path=" /delete" )
public String delete(SessionStatus status) {
@CookieValue
1.作用:用于获取指定cookie的名称的值
2.属性
2.1value: cookie的名称
3.代码视图
@RequestMapping(path=" /he1lo" )
public String sayHello(@CookieValue (value= "JSESSIONID") String cookieValue) {
System. out . println( cookieValue) ;
return "success" ;
}