SSM开发框架
- Spring:对象容器框架,可以对系统中的对象进行系统的管理,是框架的框架
- Spring MVC:Spring的一个分支产品,可以有效地管理Web层面的交互
- MyBatis:简化了数据库增删改查的操作
MyBatis介绍
- Mybatis是优秀的持久层框架
- MyBatis使用XML将SQL与程序解耦,便于维护
- MyBatis学习简单,执行高效,是JDBC的延伸
MyBatis开发流程
- 引入MyBatis依赖
- 创建核心配置文件
- 创建实体(Entity)类
- 创建Mapper映射文件
- 初始化SessionFactory
- 利用SqlSession对象操作数据
MyBatis环境配置
配置文件:mybatis-config.xml
- MyBatis采用XML格式皮遏制数据库环境信息
- MyBatis环境配置标签
- environment包含数据库驱动,URL,用户名和密码
第一步:打开IDEA,选择Maven创建一个新的项目
第二步:修改pom.xml文件
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.song</groupId>
<artifactId>mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun</name>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
</project>
第三步:新建mybatis-config.xml文件
src/main/resources/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="dev">
<!--配置环境,不同的环境不同的id名字-->
<environment id="dev">
<!-- 采用JDBC方式对数据库事务进行commit/rollback -->
<transactionManager type="JDBC"></transactionManager>
<!--采用连接池方式管理数据库连接-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="xxhacys2015"/>
</dataSource>
</environment>
<environment id="prd">
<!-- 采用JDBC方式对数据库事务进行commit/rollback -->
<transactionManager type="JDBC"></transactionManager>
<!--采用连接池方式管理数据库连接-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://192.168.1.155:3306/babytun?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>
SqlSessionFactory
SqlSessionFactory是MyBatis的核心对象
用于初始化MyBatis,创建SqlSession对象
- 保证SqlSessionFactory在应用中全局唯一
SqlSession
- SqlSession是MyBatis操作数据库的核心对象
- SqlSession使用JDBC方式与数据库交互
- SqlSession对象提供了数据表CRUD对应方法
初始化工具类MyBatisUtils
src/main/java/com.song.mybatis.utils/MyBatisUtils.java
#一般工具类都放在utils下面
package com.song.mybatis.utils;
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 java.io.IOException;
import java.io.Reader;
/**
* 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();
// 初始化错误时,通过抛出异常ExceptionInInitializerError通知调用者
throw new ExceptionInInitializerError(e);
}
}
/**
* openSession 创建一个新的SqlSession对象
* @return SqlSession对象
*/
public static SqlSession openSession(){
return sqlSessionFactory.openSession();
}
/**
* 释放一个有效的SqlSession对象
* @param session 准备释放SqlSession对象
*/
public static void closeSession(SqlSession session){
if(session != null){
session.close();
}
}
}
MyBatis数据查询
- 创建实体类(Entity)
- 创建Mapper XML
- 编写SQL标签
src/main/resources/mappers/goods.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.song.mybatis.entity.Goods">
select * from t_goods order by goods_id desc limit 10
</select>
</mapper>
到这里,就可以试试数据查询功能了
src/test/java/com.song.mybatis/MyBatisTestor.java
package com.song.mybatis;
import com.song.mybatis.entity.Goods;
import com.song.mybatis.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class MyBatisTestor {
@Test
public void testMyBatis(){
SqlSession sqlSession = null;
try{
sqlSession = MyBatisUtils.openSession();
List<Goods> list = sqlSession.selectList("goods.selectAll");
for(Goods g:list){
System.out.println(g.getTitle());
}
}catch (Exception e){
e.printStackTrace();
}finally {
MyBatisUtils.closeSession(sqlSession);
}
}
}
根据输出结果可以看到,数据库中的数据已经查询到了。但是存在一个问题,有些字段的内容没有查询到,
比如goodsId,subTitle等等。这是因为java程序中用的是goodsId,而数据库底层用的却是goods_id,它们并不一致,就导致查询不到了。那怎么解决这个问题呢?请继续往下看。
- 开启驼峰命名映射
src/main/resources/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>
<settings>
<!-- goods_id ==> goodsId 驼峰命名转换 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--设置默认指向的数据库-->
<environments default="dev">
<!--配置环境,不同的环境不同的id名字-->
<environment id="dev">
<!-- 采用JDBC方式对数据库事务进行commit/rollback -->
<transactionManager type="JDBC"></transactionManager>
<!--采用连接池方式管理数据库连接-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="xxhacys2015"/>
</dataSource>
</environment>
<environment id="prd">
<!-- 采用JDBC方式对数据库事务进行commit/rollback -->
<transactionManager type="JDBC"></transactionManager>
<!--采用连接池方式管理数据库连接-->
<dataSource type="POOLED">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://192.168.1.155:3306/babytun?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/goods.xml"/>
</mappers>
</configuration>
这样一来,所有的数据都能查询到了
SQL传参
src/main/resources/mappers/goods.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.song.mybatis.entity.Goods">
select * from t_goods order by goods_id desc limit 10
</select>
<!-- 单参数传递,使用parameterType指定参数的数据类型即可,SQL中#{value}提取参数 -->
<select id="selectById" parameterType="Integer" resultType="com.song.mybatis.entity.Goods">
select * from t_goods where goods_id = #{value}
</select>
<!-- 多参数传递,使用parameterType指定Map接口,SQL中#{key}提取参数 -->
<select id="selectByPriceRange" parameterType="java.util.Map" resultType="com.song.mybatis.entity.Goods">
select * from t_goods
where
current_price between #{min} and #{max}
order by current_price
limit 0,#{limit}
</select>
</mapper>
测试:
src/test/java/com.song.mybatis/MyBatisTestor.java
#根据结果可以看到,我们想要的数据已经查询到了
package com.song.mybatis;
import com.song.mybatis.entity.Goods;
import com.song.mybatis.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyBatisTestor {
@Test
public void testSelectById(){
SqlSession sqlSession = null;
try{
sqlSession = MyBatisUtils.openSession();
Goods goods = sqlSession.selectOne("goods.selectById", 1602);
System.out.println(goods.getTitle());
}catch (Exception e){
e.printStackTrace();
}finally {
MyBatisUtils.closeSession(sqlSession);
}
}
@Test
public void testSelectByPriceRange(){
SqlSession sqlSession = null;
try{
sqlSession = MyBatisUtils.openSession();
Map param = new HashMap<>();
param.put("min",100);
param.put("max",500);
param.put("limit",10);
List<Goods> list = sqlSession.selectList("goods.selectByPriceRange", param);
for(Goods g:list){
System.out.println(g.getTitle());
}
}catch (Exception e){
e.printStackTrace();
}finally {
MyBatisUtils.closeSession(sqlSession);
}
}
}
多表关联查询
src/main/resources/mappers/goods.xml
<!-- 利用LinkedHashMap保存多表关联结果
MyBatis会将每一条记录包装为LinkedHashMap对象
key是字段名,value是字段对应的值,字段类型根据表结构进行自动判断
优点:易于扩展,易于使用
缺点:太过灵活,无法进行编译时检查
-->
<select id="selectGoodsMap" resultType="java.util.LinkedHashMap">
select g.*, c.category_name from t_goods g, t_category c
where g.category_id = c.category_id
</select>
使用LinkedHashMap的原因:可以保持顺序
#如果直接使用Map的话,查询出来的结果是无序的
src/test/java/com.song.mybatis/MyBatisTestor.java
@Test
public void testSelectGoodsMap(){
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
List<Map> list = session.selectList("goods.selectGoodsMap");
for(Map map:list){
System.out.println(map);
}
}catch (Exception e){
e.printStackTrace();
}finally {
MyBatisUtils.closeSession(session);
}
}
ResultMap结果映射
用LinkedHashMap保存查询结果的体验并不是很好,那么我们可以使用ResultMap
- ResultMap可以将查询结果映射为复杂类型的Java对象
- ResultMap适用于Java对象保存多表关联结果
- ResultMap支持对象关联查询等高级特性
Data Transfer Object — 数据传输对象
src/main/resources/mappers/goods.xml
<!-- 结果映射 -->
<resultMap id="rmGoods" type="com.song.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="prent_id"></result>
<result property="category.categoryLevel" column="category_level"></result>
<result property="category.categoryOrder" column="category_order "></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>
com.song.mybatis.dto.GoodsDTO
#自定义数据传输对象
package com.song.mybatis.dto;
import com.song.mybatis.entity.Category;
import com.song.mybatis.entity.Goods;
// Data Transfer Object -- 数据传输对象
public class GoodsDTO {
private Goods goods = new Goods();
private Category category = new Category();
public Goods getGoods() {
return goods;
}
public void setGoods(Goods goods) {
this.goods = goods;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
}
src/test/java/com.song.mybatis/MyBatisTestor.java
@Test
public void testSelectGoodsDTO(){
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
List<GoodsDTO> list = session.selectList("goods.selectGoodsDTO");
for(GoodsDTO g:list){
System.out.println(g.getGoods().getTitle());
}
}catch (Exception e){
e.printStackTrace();
}finally {
MyBatisUtils.closeSession(session);
}
}
数据插入操作
第一步,需要修改MyBaticUtils
src/main/java/com.song.mybatis/utils/MyBatisUtils.java
/**
* openSession 创建一个新的SqlSession对象
* @return SqlSession对象
*/
public static SqlSession openSession(){
// 默认SqlSession会自动提交事务数据(commit)
// 设置false代表关闭自动提交,改为手动提交事务数据
return sqlSessionFactory.openSession(false);
}
第二步,新增sql处理
src/main/resources/mappers/goods.xml
#selectkey的作用是在写入数据之后,更新主键goodsId
<insert id="insert" parameterType="com.song.mybatis.entity.Goods">
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})
<selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
select last_insert_id()
</selectKey>
</insert>
第三步,测试插入处理
src/test/java/com.song.mybatis/MyBatisTestor.java
@Test
public void testInsert(){
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);
// insert()方法返回值代表本次成功插入的记录总数
int num = session.insert("goods.insert", goods);
session.commit(); // 提交事务数据
}catch (Exception e){
if(session != null){
session.rollback(); // 回滚事务
}
e.printStackTrace();
}finally {
MyBatisUtils.closeSession(session);
}
}
selectKey与useGeneratedKeys的区别
- selectKey标签需要明确编写最新主键的SQL语句
- useGeneratedKeys属性会自动根据驱动生成对应SQL语句
- selectKey适用于所有的关系型数据库
- useGeneratedKeys只支持“自增主键”类型的数据库
总结:
- selectKey标签是通用方案,适用于所有数据库,但编写麻烦
迁移到别的数据库,可能还需要修改selectKey里面的内容
- useGeneratedKeys属性只支持“自增主键”数据库,使用简单
心得:
- 优先推荐使用useGeneratedKeys
- 但是考虑的复杂的业务场景,如果有很多种数据库要对数据进行支撑和保存的时候,就用selectKey
扩展:
在Oracle中selectKey的用法
数据更新操作
src/main/resources/mappers/goods.xml
<update id="update" parameterType="com.song.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
src/test/java/com.song.mybatis/MyBatisTestor.java
@Test
public void testUpdate(){
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
Goods goods = session.selectOne("goods.selectById",739);
goods.setTitle("更新测试商品");
int num = session.update("goods.update",goods);
session.commit(); // 提交事务数据
}catch (Exception e){
if(session != null){
session.rollback(); // 回滚事务
}
e.printStackTrace();
}finally {
MyBatisUtils.closeSession(session);
}
}
数据删除操作
src/main/resources/mappers/goods.xml
<delete id="delete" parameterType="Integer">
delete from t_goods where goods_id = #{value}
</delete>
src/test/java/com.song.mybatis/MyBatisTestor.java
@Test
public void testDelete(){
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
int num = session.delete("goods.delete",739);
session.commit(); // 提交事务数据
}catch (Exception e){
if(session != null){
session.rollback(); // 回滚事务
}
e.printStackTrace();
}finally {
MyBatisUtils.closeSession(session);
}
}
预防SQL注入攻击
SQL注入是指攻击者利用SQL漏洞,绕过系统约束,越权获取数据的攻击方式
MyBatis两种传值方式