MyBatis 概述
- MyBatis是优秀的持久层框架
- MyBatis使用XML将SQL与程序解耦,便于维护
- MyBatis学习简单,执行高效,是JDBC的延伸
MyBatis 开发流程
文档目录结构:
1. 引入MyBatis依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
通过下面设置可以解决同外网下载速度慢的问题:
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun</name>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
</repositories>
2. 创建核心配置文件
<?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>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
<environment id="prd">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://presentx.top:3306/babytun?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="present"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/goods.xml"></mapper>
</mappers>
</configuration>
前四行的代码抄上即可 设置驼峰命名法转换:
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
default 指出默认数据源,方便切换不同数据库() 开发环境和线上生产环境
<environments default="dev">
Mybatis的配置文件里,&符号后面要加上amp;
3. 创建实体(Entity)类
对应数据库的列名,严格按照驼峰命名法的规则声明entity实体类的成员变量。 代码略。
4. 创建Mapper映射文件
基本的增删改查,XML代码示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="goods">
<select id="selectAll" resultType="com.br.mybatis.entity.Goods" useCache="false">
select * from t_goods order by goods_id desc limit 10
</select>
<select id="selectById" parameterType="Integer" resultType="com.br.mybatis.entity.Goods">
select * from t_goods where goods_id = #{value}
</select>
<select id="selectByPriceRange" parameterType="java.util.Map" resultType="com.br.mybatis.entity.Goods">
select * from t_goods
where
current_price between #{min} and #{max}
order by current_price
limit 0,#{limt}
</select>
<select id="selectGoodsMap" resultType="java.util.LinkedHashMap" flushCache="true">
select g.* , c.category_name from t_goods g , t_category c
where g.category_id = c.category_id
</select>
<resultMap id="rmGoods" type="com.br.mybatis.dto.GoodsDTO">
<id property="goods.goodsId" column="goods_id"></id>
<result property="goods.title" column="title"></result>
<result property="goods.originalCost" column="original_cost"></result>
<result property="goods.currentPrice" column="current_price"></result>
<result property="goods.discount" column="discount"></result>
<result property="goods.isFreeDelivery" column="is_free_delivery"></result>
<result property="goods.categoryId" column="category_id"></result>
<result property="category.categoryId" column="category_id"></result>
<result property="category.categoryName" column="category_name"></result>
<result property="category.parentId" column="parent_id"></result>
<result property="category.categoryLevel" column="category_level"></result>
<result property="category.categoryOrder" column="category_order"></result>
<result property="test" column="test"/>
</resultMap>
<select id="selectGoodsDTO" resultMap="rmGoods">
select g.* , c.*,'1' as test from t_goods g , t_category c
where g.category_id = c.category_id
</select>
<insert id="insert" parameterType="com.br.mybatis.entity.Goods" flushCache="true"
useGeneratedKeys="true" keyProperty="goodsId">
INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
VALUES (#{title} , #{subTitle} , #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery}, #{categoryId})
</insert>
<update id="update" parameterType="com.br.mybatis.entity.Goods">
UPDATE t_goods
SET
title = #{title} ,
sub_title = #{subTitle} ,
original_cost = #{originalCost} ,
current_price = #{currentPrice} ,
discount = #{discount} ,
is_free_delivery = #{isFreeDelivery} ,
category_id = #{categoryId}
WHERE
goods_id = #{goodsId}
</update>
<delete id="delete" parameterType="Integer">
delete from t_goods where goods_id = #{value}
</delete>
<select id="selectByTitle" parameterType="java.util.Map" resultType="com.br.mybatis.entity.Goods">
select * from t_goods where title = #{title}
${order}
</select>
</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="goods">
</mapper>
然后里面就是按照约定写SQL了。后面再说
5. 初始化SessionFactory
@Test
public void testSqlSessionFactory() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
System.out.println("SqlSessionFactory加载成功");
SqlSession sqlSession = sqlSessionFactory.openSession();
Connection connection = sqlSession.getConnection();
System.out.println(connection);
}
上面这段代码我们会进行封装。通过工具类完成对 SqlSession 对象的创建与销毁。 封装类的代码如下:
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory = null;
static {
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
throw new ExceptionInInitializerError(e);
}
}
public static SqlSession openSession() {
return sqlSessionFactory.openSession();
}
public static void closeSqlSession(SqlSession sqlSession) {
if(sqlSession!=null){
sqlSession.close();
}
}
}
6. 利用SqlSession对象操作数据
对 SqlSessionFactory 和 SqlSession 对象的几点说明:
增删改查
1.查询
1.1 没有参数传递的 查询
<select id="selectAll" resultType="com.br.mybatis.entity.Goods" useCache="false">
select * from t_goods order by goods_id desc limit 10
</select>
select标签中指明了返回值的类型 里面写 SQL 语句
@Test
public void testSelectAll(){
SqlSession sqlSession = null;
try{
sqlSession = MyBatisUtils.openSession();
List<Goods> goodsList = sqlSession.selectList("goods.selectAll");
for (Goods good:goodsList){
System.out.println(good);
}
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSqlSession(sqlSession);
}
}
如果 selectAll 名称唯一,可以省略命名空间 goods
1.2 传递单个参数查询
<select id="selectById" parameterType="Integer" resultType="com.br.mybatis.entity.Goods">
select * from t_goods where goods_id = #{value}
</select>
parameterType 指明传入的参数类型 resultType 返回值的类型 参数名称 默认为 value
@Test
public void testSelectById(){
SqlSession sqlSession = null;
try{
sqlSession = MyBatisUtils.openSession();
Goods good = sqlSession.selectOne("goods.selectById",1603);
System.out.println(good.getTitle());
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSqlSession(sqlSession);
}
}
1.3 传递多个参数查询
<select id="selectByPriceRange" parameterType="java.util.Map" resultType="com.br.mybatis.entity.Goods">
select * from t_goods
where
current_price between #{min} and #{max}
order by current_price
limit 0,#{limt}
</select>
传入的参数类型为Map parameterType="java.util.Map"
@Test
public void testSelectByPriceRange(){
SqlSession sqlSession = null;
try{
sqlSession = MyBatisUtils.openSession();
Map map = new HashMap();
map.put("min",100);
map.put("max",500);
map.put("limt",10);
List<Goods> goodsList = sqlSession.selectList("selectByPriceRange",map);
for (Goods goods:goodsList){
System.out.println(goods);
}
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSqlSession(sqlSession);
}
}
1.4 多表关联查询
<select id="selectGoodsMap" resultType="java.util.LinkedHashMap" flushCache="true">
select g.* , c.category_name from t_goods g , t_category c
where g.category_id = c.category_id
</select>
MyBatis会将每一条记录包装为LinkedHashMap对象 key是字段名 value是字段对应的值 , 字段类型根据表结构进行自动判断
@Test
public void testSelectGoodsMap(){
SqlSession sqlSession = null;
try{
sqlSession = MyBatisUtils.openSession();
List<Map> mapList= sqlSession.selectList("selectGoodsMap");
for (Map map:mapList){
System.out.println(map.keySet());
System.out.println(map);
}
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSqlSession(sqlSession);
}
}
这里把结果存在了map而不是实体类中,对以后的使用不方便。 可以创建一个新的实体类来接受多表关联查询的结果 GoodsDTO的成员变量:
private Goods goods = new Goods();
private Category category = new Category();
private String test;
于是就有了下面的方案
1.5 多表关联查询(改进)
<resultMap id="rmGoods" type="com.br.mybatis.dto.GoodsDTO">
<id property="goods.goodsId" column="goods_id"></id>
<result property="goods.title" column="title"></result>
<result property="goods.originalCost" column="original_cost"></result>
<result property="goods.currentPrice" column="current_price"></result>
<result property="goods.discount" column="discount"></result>
<result property="goods.isFreeDelivery" column="is_free_delivery"></result>
<result property="goods.categoryId" column="category_id"></result>
<result property="category.categoryId" column="category_id"></result>
<result property="category.categoryName" column="category_name"></result>
<result property="category.parentId" column="parent_id"></result>
<result property="category.categoryLevel" column="category_level"></result>
<result property="category.categoryOrder" column="category_order"></result>
<result property="test" column="test"/>
</resultMap>
<select id="selectGoodsDTO" resultMap="rmGoods">
select g.* , c.*,'1' as test from t_goods g , t_category c
where g.category_id = c.category_id
</select>
与前面不同的是,出了写SQL语句,这里还需要指明映射。 resultMap=“rmGoods” 指出使用上面刚配置的那个映射。
@Test
public void testSelectGoodsDTO(){
SqlSession session = null;
try {
session = MyBatisUtils.openSession();
List<GoodsDTO> goodsDTOList = session.selectList("selectGoodsDTO");
for (GoodsDTO goodsDTO:goodsDTOList){
System.out.println(goodsDTO.getGoods().getTitle()+"--"+goodsDTO.getCategory().getCategoryName()+"--"+goodsDTO.getTest());
}
}catch (Exception e){
e.printStackTrace();
}finally {
MyBatisUtils.closeSqlSession(session);
}
}
写入操作有:插入,删除,更新。 先看一下数据库事务: 为了保证数据的一致性,要注意 commit 和 rollback
2.插入
<insert id="insert" parameterType="com.br.mybatis.entity.Goods" flushCache="true"
useGeneratedKeys="true" keyProperty="goodsId">
INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
VALUES (#{title} , #{subTitle} , #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery}, #{categoryId})
</insert>
指明传入的参数来源 parameterType="com.br.mybatis.entity.Goods" ps:因为在插入数据的时候 主键id 是数据库自己生成的,所以我们需要把这个数据库自动生成的值回填到实体类中。 方便的方法 标签中加入useGeneratedKeys=“true” keyProperty=“goodsId” (只适用于数据库主键自增)
<insert id="insert" parameterType="com.br.mybatis.entity.Goods" flushCache="true"
useGeneratedKeys="true" keyProperty="goodsId">
稍微麻烦但是通用的方法:
<selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
select last_insert_id()
</selectKey>
@Test
public void testInsert() throws Exception {
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
Goods goods = new Goods();
goods.setTitle("测试商品");
goods.setSubTitle("测试子标题");
goods.setOriginalCost(200f);
goods.setCurrentPrice(100f);
goods.setDiscount(0.5f);
goods.setIsFreeDelivery(1);
goods.setCategoryId(43);
int num = session.insert("goods.insert", goods);
session.commit();
System.out.println(goods);
}catch (Exception e){
if(session != null){
session.rollback();
}
throw e;
}finally {
MyBatisUtils.closeSqlSession(session);
}
}
记得 commit 和 rollback
3.更新
<update id="update" parameterType="com.br.mybatis.entity.Goods">
UPDATE t_goods
SET
title = #{title} ,
sub_title = #{subTitle} ,
original_cost = #{originalCost} ,
current_price = #{currentPrice} ,
discount = #{discount} ,
is_free_delivery = #{isFreeDelivery} ,
category_id = #{categoryId}
WHERE
goods_id = #{goodsId}
</update>
数据类源于实体类Goods
@Test
public void testUpdate(){
SqlSession session = null;
try {
session = MyBatisUtils.openSession();
Goods goods = session.selectOne("goods.selectById",739);
goods.setTitle("更新的标题");
session.update("goods.update",goods);
session.commit();
}catch (Exception e){
if(session!=null){
session.rollback();
}
}finally {
MyBatisUtils.closeSqlSession(session);
}
}
4.删除
<delete id="delete" parameterType="Integer">
delete from t_goods where goods_id = #{value}
</delete>
根据 id 进行删除,指明接受的数据类型 parameterType=“Integer”
@Test
public void testDelete(){
SqlSession session = null;
try {
session = MyBatisUtils.openSession();
session.update("goods.delete",739);
session.commit();
}catch (Exception e){
if(session!=null){
session.rollback();
}
}finally {
MyBatisUtils.closeSqlSession(session);
}
}
预防SQL注入
<select id="selectByTitle" parameterType="java.util.Map" resultType="com.br.mybatis.entity.Goods">
select * from t_goods where title = #{title}
${order}
</select>
#{title} 预编译传递参数,可以防止SQL注入。 ${order} 原文传递,可用于拼接SQL语句。(不推荐使用,如果必须使用,传递的参数不能通过前台输入获得)
MyBatis的工作流程
|