简介
- MyBatis是一个基于Java的持久层框架。
- MyBatis采用配置文件动态管理SQL语句,并含有输入映射、输出映射机制以及数据库连接池配置。
- ORM(Object/Relational Mapping)对象/关系映射是一种数据持久化技术,它在对象模型和关系型数据库之间建立了对应关系,并且它提供了一种机制,通过JavaBean对象操作数据库表中的数据。MyBatis是ORM解决方案。通过MyBatis可以建立SQL关系映射,便捷地实现了数据的增删改查等操作。
代码实现
- maven依赖 pom.xml
<!-- 事务 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- mybatis集成spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- 导入dbcp数据库连接池的依赖,用来在spring-mybatis.xml中配置数据库 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!--j2ee相关包 servlet、jsp、jstl-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
xml配置实现
- bean配置 spring.xml
<!-- 1 引入jdbc配置文件 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
<!-- 2.配置数据源-数据库连接池 -->
<bean id="dbSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="${maxIdle}"/>
<!-- 初始化连接数 -->
<property name="initialSize" value="${initialSize}"/>
<!--最大连接等待时间,连接超时时间 单位:ms-->
<property name="maxWait" value="${maxWait}"></property>
</bean>
<!-- 3 spring和MyBatis整合,通过spring来管理MyBatis的SqlSessionFactory会话工厂 -->
<bean id="ssf" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定数据库连接池引用 -->
<property name="dataSource" ref="dbSource" />
<!-- 引入mybatis配置文件 -->
<property name="configLocation" value="classpath:mybatis.xml"/>
<!-- 自动扫描mapping.xml文件 -->
<!--<property name="mapperLocations" value="classpath:mapper/*.xml"></property>-->
</bean>
<!--sqlSession模板对象,sqlSession可以实现数据库连接以及包含所有执行SQL操作的方法-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="ssf" />
</bean>
- 参数文件 jdbc.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/project1?serverTimezone=GMT%2B8&allowMultiQueries=true
username=root
password=123456
initialSize=0
maxActive=20
maxIdle=20
minIdle=1
maxWait=60000
- mybatis配置文件 mybatis.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>
<!--日志打印sql语句-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!-- mapping文件(ORM对象关系映射文件)路径配置 -->
<mappers>
<mapper resource="mapper/StaffMapper.xml" />
</mappers>
</configuration>
- mapping文件 StaffMapper.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="com.st.dao.IStaffDao"><!--namespace属性关联dao层-->
<!--orm 对象和数据表之间的映射关系-->
<resultMap id="baseMap" type="com.st.model.Staff">
<!--
column 指定数据表字段
property 指定类属性
jdbcType 指定数据类型 int-INTEGER string-VARCHAR
-->
<result column="staff_id" property="staffId" jdbcType="INTEGER"/>
<result column="staff_name" property="staffName" jdbcType="VARCHAR"/>
<result column="phone" property="phone" jdbcType="VARCHAR"/>
</resultMap>
<!--
parameterType 参数类型
resultMap 结果集映射 对应的是resultMap.id
-->
<select id="queryOne" parameterType="java.lang.Integer" resultMap="baseMap">
SELECT * FROM staff WHERE staff_id = #{staffId}
</select>
<select id="queryAll" parameterType="com.st.model.Staff" resultMap="baseMap">
<!-- #{} 会自动在变量前后拼一个英文单引号-->
SELECT * FROM staff
<where> <!-- where标签会把标签中的第一条if语句中的and去掉-->
<if test="staffId != null ">AND staff_id = #{staffId} </if>
<if test="staffName != null ">AND staff_name = #{staffName} </if>
<if test="phone != null ">AND phone = #{phone} </if>
</where>
</select>
<!--插入语句没有返回值,但是会自动返回执行sql语句影响的行数,数据类型是integer-->
<insert id="add" parameterType="com.st.model.Staff">
insert into staff
<!--
prefix 前缀
suffix 后缀
suffixOverrides 去除拼接语句中的最后一个逗号
-->
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="staffName != null">staff_name,</if>
<if test="phone != null">phone,</if>
</trim>
<trim prefix="VALUES(" suffix=")" suffixOverrides=",">
<if test="staffName != null">#{staffName},</if>
<if test="phone != null">#{phone},</if>
</trim>
</insert>
<!--批量插入-->
<insert id="addBatch" parameterType="java.util.List">
<!--
使用批量插入需要在jdbc:url中添加allowMultiQueries=true
作用:
1.可以在sql语句后携带分号,实现多语句执行。
2.可以执行批处理,同时发出多个SQL语句。
collection 被遍历的结果集
item 本次遍历获取的元素别名
index 当前迭代的次数
separator 在每一句sql语句后拼接分号(;)
-->
<foreach collection="list" item="staff" index="index" separator=";">
INSERT INTO staff
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="staff.staffId != null">staff_id,</if>
<if test="staff.staffName != null">staff_name,</if>
<if test="staff.phone != null">phone,</if>
</trim>
<trim prefix="VALUES(" suffix=")" suffixOverrides=",">
<if test="staff.staffId != null">#{staff.staffId, jdbcType=INTEGER},</if>
<if test="staff.staffName != null">#{staff.staffName, jdbcType=VARCHAR},</if>
<if test="staff.phone != null">#{staff.phone, jdbcType=VARCHAR},</if>
</trim>
</foreach>
</insert>
<!--
插入同时返回主键
useGeneratedKeys 设置是否使用JDBC的getGeneratedKeys方法获取主键并赋值到keyProperty设置属性中
keyProperty 实体类主键属性
-->
<insert id="addReturnPK" parameterType="com.st.model.Staff" useGeneratedKeys="true" keyProperty="staffId">
<!-- insert into staff(staff_name, phone) values(#{staffName},#{phone});-->
insert into staff
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="staffName != null">staff_name,</if>
<if test="phone != null">phone,</if>
</trim>
<trim prefix="VALUES(" suffix=")" suffixOverrides=",">
<if test="staffName != null">#{staffName},</if>
<if test="phone != null">#{phone},</if>
</trim>
</insert>
<!--更新-->
<update id="update" parameterType="com.st.model.Staff">
update staff
<set>
<if test="staffName != null">staff_name = #{staffName},</if>
<if test="phone != null">phone = #{phone},</if>
</set>
<where>
<if test="staffId != null">and staff_id = #{staffId}</if>
</where>
</update>
</mapper>
- Dao中调用sqlSession
package com.st.dao.impl;
import com.st.dao.IStaffDao;
import com.st.model.Staff;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class StaffDao implements IStaffDao {
@Autowired
private SqlSession sqlSession;
private String namespace = "com.st.dao.IStaffDao.";
@Override
public Staff queryOne(Integer id) {
//指定mapper的namespace和传入参数
Staff s = sqlSession.selectOne(namespace + "queryOne", id);
return s;
}
@Override
public List<Staff> queryAll(Staff s) {
return sqlSession.selectList(namespace + "queryAll", s);
}
@Override
public int add(Staff s) {
return sqlSession.insert(namespace + "add", s);
}
@Override
public int addBatch(List<Staff> list) {
return sqlSession.insert(namespace + "addBatch", list);
}
@Override
public int addReturnPK(Staff s) {
return sqlSession.insert(namespace + "addReturnPK", s);
}
@Override
public int update(Staff s) {
return sqlSession.update(namespace + "update", s);
}
}
注解实现
待续
${} 与 #{} 的区别
- #{}与${}都是从参数列表中取值;
- #{}是编译好SQL语句再取值;${}是取值以后再去编译SQL语句;
- #{}是经过预编译的,是安全的。#{}引用参数的时候,Mybatis会把这个参数认为是一个字符串,并自动加上单引号’’;
- ${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入的风险;
- 举例:
select * from staff where id=#{id}
传入id = " or 1=1"时,sql为
select * from staff where id='1 or 1=1'
但对于 ${}
select * from staff where id=${id}
传入id = " or 1=1"时,sql为
select * from staff where id=1 or 1=1
此时 ${} 显然是不安全的
mybatis一级缓存与二级缓存
- 设置缓存后,在参数和SQL完全一样的情况下, 多次调用mapper方法,只会执行一次SQL,第一次执行sql查询到的结果集会被保存在缓存中,后面重复调用mapper方法返回的是缓存中的结果集。
- 缓存的意义:提高对数据库查询的效率,提高应用程序的性能
一级缓存
1 一级缓存是默认开启的;
2 一级缓存只是相对于同一个SqlSession而言
二级缓存
1 二级缓存默认不开启的;
2 二级缓存是Application级别的缓存;
3 二级缓存要求返回的POJO(简单java类)必须是可序列化的