MyBaits是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架;
jdbc缺陷:存在大量的硬编码灵活性低,大量的无关业务的处理编码,如连接和关闭数据库的操作,扩展优化不便;
优点:
1、避免传统JDBC硬编码,执行高效,是JDBC的延伸 2、使用XML将SQL与程序解耦,便于维护
3、POJO对象和数据库记录直接映射 4、完善的文档支持
应用场景:1.更加关注SQL优化的项目;2.需求频繁改动的项目
框架是一种规则,保证开发者遵循相同的方式开发程序,框架提倡”不要重复造轮子”,对基础功能进行封装,极大提高了开发效率,统一的编码规则,利于团队管理和维护。
SSM开发框架:
新建数据库连接时,注意加上时区测试
useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
比如连接icake数据库的URL
jdbc:mysql://localhost:3306/icake?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
引入MyBatis依赖,利用maven进行管理
pom.xml中加入阿里云的仓库连接,下载依赖包更稳更快
<repositories>
<repository>
<id>aliyunid>
<name>aliyunname>
<url>https://maven.aliyun.com/repository/publicurl>
repository>
repositories>
创建核心配置文件,使用xml文件配置。
比如:配置mybatis-config.xml文件
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC">transactionManager>
...
创建实体(Entity)类,设置属性,生成get、set方法
public class Goods {
private Integer goodsId;//商品编号
private String title;//标题
...
public Integer getGoodsId() {
return goodsId;
}
...
}
创建Mapper映射文件,实体类和数据表的映射
<mapper namespace="goods">
<select id="selectAll" resultType="com.imooc.mybatis.entity.Goods" >
select * from t_goods order by goods_id desc limit 10
select>
...
mapper>
初始化SessionFactory,会话工厂。读取配置文件。比如读取mybatis-config.xml
//初始化工具类MyBatisUtils,在工具类中配置,保证SqlSessionFactory在应用中全局唯一
public class MyBatisUtils {
//利用static(静态)属于类不属于对象,且全局唯一
private static SqlSessionFactory sqlSessionFactory = null;
//利用静态块在初始化类时实例化sqlSessionFactory
static {
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
...
public static SqlSession openSession(){
//默认SqlSession对自动提交事务数据(commit),设置false代表关闭自动提交,改为手动提交事务数据
return sqlSessionFactory.openSession(false);
}
利用SqlSession对象操作数据,增,删,改,查。
//新建测试类MyBatisTestor,创建testSelectAll方法,编写java代码对数据库进行操作
public void testSelectAll() throws Exception {
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
//goods.selectAll定位sql语句,返回的是一堆Goods对象,用list集合来存放
//session.selectList返回多条数据,session.selectOne()方法返回一条数据
List<Goods> list = session.selectList("goods.selectAll");
//for循环,getTitle()方法,输出对应表的标题
for(Goods g : list){
System.out.println(g.getTitle());
}
}catch (Exception e){
throw e;
}finally {
MyBatisUtils.closeSession(session);
}
}
MyBatis采用XML格式配置数据库环境信息,MyBaits环境配置标签,environment包含数据库驱动、URL、用户名与密码,, 。idea内置了连接数据库的方法。
例子:
<environment id="dev">
<transactionManager type="JDBC">transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
SqlSessionFactory是MyBatis的核心对象,用于加载配置文件,初始化MyBatis框架,创建SqlSession对象,保证SqlSessionFactory在应用中全局唯一
SqlSession是MyBatis操作数据库的核心对象,SqlSession使用JDBC方式与数据库交互,SqlSession对象提供了数据表CRUD对应方法,CURD是创建(Create)、更新(Update)、读取(Retrieve)和删除(Delete)操作。
初始化工具类MyBatisUtils,在工具类中配置,保证SqlSessionFactory在应用中全局唯一
/**
* MyBatisUtils工具类,创建全局唯一的SqlSessionFactory对象
*/
public class MyBatisUtils {
//利用static(静态)属于类不属于对象,且全局唯一
private static SqlSessionFactory sqlSessionFactory = null;
//利用静态块在初始化类时实例化sqlSessionFactory
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);
//初始化错误时,通过抛出异常ExceptionInInitializerError通知调用者
}
}
/**
* openSession 创建一个新的SqlSession对象
* @return SqlSession对象
*/
public static SqlSession openSession(){
//默认SqlSession对自动提交事务数据(commit)
//设置false代表关闭自动提交,改为手动提交事务数据
return sqlSessionFactory.openSession(false);
}
/**
* 释放一个有效的SqlSession对象
* @param session 准备释放SqlSession对象
*/
public static void closeSession(SqlSession session){
if (session != null) {
session.close();
}
}
}
创建实体类(Entity),类中的属性是根据数据表的字段创建的
创建Mapper XML
作用:说明创建的类中的属性和数据库中表的对应关系,说明了对象和表之间的映射关系。mapper文件中存放了要执行的sql语句
注意:xml文件最上面,加上xml声明和mybatis的dtd声明,dtd是约束文件,在dtd中定义对xml文件的约束,这样可以保证xml文件内容的有效性
<mappers>
<mapper resource="mappers/goods.xml"/>
mappers>
<mapper namespace="goods">
<select id="selectAll" resultType="com.imooc.mybatis.entity.Goods" >
select * from t_goods order by goods_id desc limit 10
select>
mapper>
开启驼峰命名映射:比如:数据库表的某一字段名为:goods_id,创建类的对象属性为:goodsId(驼峰命名法)。名字不对应导致查询不到数据库中的数据。开启驼峰命名映射后,会把goods_id映射为goodsId
在mybatis-config.xml文件中配置驼峰命名映射
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
单参数传递,使用parameterType指定参数的数据类型即可,SQL中#{value}提取参数
1.mapper映射文件中配置映射:
<select id="selectById" parameterType="Integer"
resultType="com.imooc.mybatis.entity.Goods">
select * from t_goods where goods_id = #{value}
select>
2.对应的java调用代码:
SqlSession session = MyBatisUtils.openSession();
//session.selectOne()方法返回一条数据
Goods goods = session.selectOne("goods.selectById",1602);
//getTitle()方法,输出对应表的标题
System.out.println(goods.getTitle());
多参数传递,使用parameterType指定Map接口,SQL中#{key}提取参数。parameterType=“java.util.Map”
注意:若映射文件中,SQL语句对应的id是全局唯一的,调用时也可以不写命名空间
Map的适用场景:为SQL语句传入多个参数,查询返结果包含跨表字段
为SQL语句传入多个参数
//java中的调用代码:
SqlSession session = MyBatisUtils.openSession();
Map param = new HashMap();
param.put("min", 100);
param.put("max", 500);
param.put("limt", 10);
//selectByPriceRange前面可以不加goods这个namespace,前提是保证你这个sql全局唯一
List<Goods> list = session.selectList("selectByPriceRange", param);
for (Goods goods:list) {
System.out.println(goods.getTitle() + ": " + goods.getCurrentPrice());
}
查询返结果包含跨表字段
<select id="findGoodsMap" resultType="java.util.LinkedHashMap">
select g.* , c.category_name
from t_goods g , t_category c
where g.category_id = c.category_id
</select>
session = MyBatisUtils.openSession();
//session.selectList返回多条数据,session.selectOne()方法返回一条数据
//list集合中存放的是Map类型的数据,即存放的是键值对类型的数据
List<Map> list = session.selectList("goods.selectGoodsMap");
for (Map map : list) {
System.out.println(map);
}
利用LinkedHashMap保存多表关联结果,当resultType为Map时,查询出来的数据是无序的。
MyBatis会将每一条记录包装为LinkedHashMap,键-值对象
key是字段名 value是字段对应的值 , 字段类型根据表结构进行自动判断
优点: 易于扩展,易于使用
缺点: 太过灵活,无法进行编译时检查
goods.xml文件配置ResultMap结果映射
/**
* Goods类的扩展,DTO,即数据传输对象,用来将sql查询结果映射为Java对象,GoodsDTO承载了多表关联查询的结果
* 所有在sql中出现的字段,都可以在这里找到对应的属性
*/
public class GoodsDTO {
//Goods goods = new Goods(),实例化一个对象,goods即有了在Goods里面创建的许多属性和方法
private Goods goods = new Goods();
private Category category = new Category();
private String test;
public Goods getGoods() {
return goods;
...
}
<resultMap id="rmGoods" type="com.imooc.mybatis.dto.GoodsDTO">
<id property="goods.goodsId" column="goods_id">id>
<result property="goods.title" column="title">result>
...
<result property="category.categoryId" column="category_id">result>
<result property="category.categoryName" column="category_name">result>
...
resultMap>
<select id="selectGoodsDTO" resultMap="rmGoods">
select g.* , c.* from t_goods g , t_category c
where g.category_id = c.category_id
select>
session = MyBatisUtils.openSession();
//session.selectList返回多条数据,session.selectOne()方法返回一条数据
List<GoodsDTO> list = session.selectList("goods.selectGoodsDTO");
for (GoodsDTO g: list) {
System.out.println(g.getGoods().getTitle());
}
作用:ResultMap可以将复杂的查询结果映射为JavaDTO对象,可以轻松的获取到属性,不像map一样容易出错,缺点就是需要额外的设置映射规则
ResultMap常用于保存多表关联结果,是Map对象的替代,Map拥有更好扩展性,ResultMap则拥有更好的可读性
多人协作的时候是有必要的,小项目可以使用map
数据库事务是保证数据操作完整性的基础,MySQL workbench等客户端编写sql语句,执行后,成为事务日志,事务日志通过commit命令再提交到数据库表中
插入 -
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})
select last_insert_id()
SqlSession session = MyBatisUtils.openSession();
Goods goods = new Goods();
//这里没有设置id,在goods.xml文件中通过selectKey标签自动添加到goodsId
goods.setTitle("测试商品");
goods.setSubTitle("测试子标题");
goods.setOriginalCost(200f);
goods.setCurrentPrice(100f);
goods.setDiscount(0.5f);
goods.setIsFreeDelivery(1);
goods.setCategoryId(43);
//insert()方法返回值代表本次成功插入的记录总数
int num = session.insert("goods.insert",goods);
//执行插入数据后,还要通过commit把新增数据的事务日志提交到数据库表中
session.commit();
注意:catch块中加上回滚
//commit提交后,数据库事务日志会清空,如果不为空,则说明异常,需要回滚,从新写入事务日志,再提交数据库
if (session != null) {
session.rollback();
}
注意:sqlSessionFactory.openSession(false);默认SqlSession对自动提交事务数据(commit),设置false代表关闭自动提交,改为手动提交事务数据
更新 -
<update id="update" parameterType="com.imooc.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>
session = MyBatisUtils.openSession();
//先获取739id对应的一条数据,返回的是一个goods对象
Goods goods = session.selectOne("goods.selectById", 739);
//修改id为739的标题
goods.setTitle("测试标题");
//执行更新sql语句,把修改好的goods对象数据传入到数据表中
int num = session.update("goods.update", goods);
//执行更新数据后,还要通过commit把新增数据的事务日志提交到数据库表中
session.commit();
删除 -
<delete id="delete" parameterType="Integer">
delete from t_goods where goods_id = #{value}
</delete>
//执行delete操作,删除id为739的那一条数据
int num = session.delete("goods.delete", 739);
//执行更新数据后,还要通过commit把新增数据的事务日志提交到数据库表中
session.commit();
SQL注入攻击是指攻击者利用SQL语句,导致输入的变成了sql语句的一部分
MyBatis两种传值方式
session = MyBatisUtils.openSession();
Map param = new HashMap();
/*
${}原文传值
select * from t_goods
where title = '' or 1 =1 or title = '【德国】爱他美婴幼儿配方奶粉1段800g*2罐 铂金版'
*/
/*
#{}预编译
select * from t_goods
where title = "'' or 1 =1 or title = '【德国】爱他美婴幼儿配方奶粉1段800g*2罐 铂金版'"
*/
param.put("title","'' or 1=1 or title='【德国】爱他美婴幼儿配方奶粉1段800g*2罐 铂金版'");
param.put("order" , " order by title desc");
List<Goods> list = session.selectList("goods.selectByTitle", param);
for(Goods g:list){
System.out.println(g.getTitle() + ":" + g.getCurrentPrice());
}
工作流程:mybatis-config.xml—SqlSessionFactory—SqlSession—执行配置好的SQL —SessionClose