他是我们软件开发的一套解决方案,不同的框架解决的是不同的问题。
使用框架的好处:框架封装了很多细节,使开发者可以使用极简的方式实现功能,提高开发效率
表现层:Spring MVC
用于展示数据
业务层:
处理业务需求
持久层:Mybatis
与数据库交互
JDBC技术:
Conncetion
PreparedStatment
ResultSet
Spring的Jdbctemplate
Spring中对jdbc的简单封装
Apache的DBUtils
也是对jdbc的简单封装
传统JDBC的连接数据库 获取预处理对象 执行语句 返回结果集代码 有大量重复 效率不高 并且还要学习sql语句
为了解决这些问题 就出现了这个框架
4Mybatis概述 mybatis是一颗持久层框架 使用java编写
它封装了很多jdbc操作的很多细节 ,使开发者只需要关注sql语句 无需关注 注册驱动 创建连接 等复杂过程
它使用了ORM思想实现了结果集的封装
ORM:
对象关系映射
就是把数据库表的列明和类的属性对应起来 表名和类名对应
让我们通过操作实体类而操作数据库表
1,新建一个普通的maven项目
2.删除src目录
3.导入maven依赖
<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.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>MyprojectartifactId>
<packaging>pompackaging>
<version>1.0-SNAPSHOTversion>
<modules>
<module>Mybatismodule>
modules>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.2version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
dependencies>
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
resources>
build>
project>
2.2.1编写mybaties工具类
package 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.InputStream;
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//官网拿下来的 死的语句
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
//有了SqlSessionFactory 我们就鞥获得sqlsession实例 这个实例包含了执行sql的所有方法
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(); //相当于得到了connection对象
}
}
2.2.2编写Mybatis核心配置文件
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=true&useUnicode=true&characterEncoding=UTF-8" />
<property name="username" value="root" />
<property name="password" value="123456" />
dataSource>
environment>
environments>
<mappers>
<mapper resource="dao/UserMapper.xml"/>
mappers>
configuration>
实体类
package pojo;
/*
定义实体类
*/
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
Dao接口
package dao;
import pojo.User;
import java.util.List;
/*
操作数据库对象实体
*/
public interface UserDao {
List<User> getUserList();
}
接口实现类以前是实现类 现在是xml文件
<mapper namespace="dao.UserDao">
<select id="getUserList" resultType="pojo.User">
select * from user
select>
mapper>
package dao;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import pojo.User;
import utils.MybatisUtils;
import java.util.List;
public class UserDaoTest {
@Test
public void test(){
//第一步 获得sqlsession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//第二步 为了调用sql语句 所以我们需要类的对象
//因为 mapper.xml实现了改接口 又因为针对接口编程
//所以我们获取的是接口的类 内部会自动使用实现类的方法
UserDao mapper = sqlSession.getMapper(UserDao.class);//实际返回的是xml对象
//第三步执行sql并遍 历
List<User> list = mapper.getUserList();
for(User user:list){
System.out.println(user);
}
//关闭
sqlSession.close();
}
}
org.apache.ibatis.binding.BindingException: Type interface dao.UserDao is not known to the MapperRegistry.
每一个mapper.xml(就是接口的实现文件) 都需要在Mybatis核心配置中注册 否则报错
初始化失败问题找不到mapper.xml配置文件
虽然我们有这个文件但是运行时没有加载到target目录中 手动配置资源过滤即可
报错:某行某列无法解析 等等 都是因为核心配置文件内容有错 注释不可以写中文
SqlSession代表和数据库的一次会话 用完关闭
Sqlsession和Connection一样都是非线程安全的
非线程安全 不能把这个变量定义在方法外 A用完关了 B就用不了了 每次使用都应该获取新的对象
mapper接口没有实现类 ,但是 mybatis会为这个接口生成一个代理对象
UserDao mapper = sqlSession.getMapper(UserDao.class)(接口和xml必须绑定)
俩个重要的配置文件
全局配置文件 :包含数据库连接 信息事务管理等信息 系统运行环境信息
接口的配置文件 保存了每一个sql语句映射信息
将sql抽取出来
单个参数mybatis不会做特殊处理
#{参数名}:取出参数值 下面的id随意些 因为只有一个参数且为基本类型 也就无所谓了
delete from tb1_employee where id =#{asdasfsa}
多个参数值 会做特殊处理
多个参数会被封装为一个map
key:param1.。。。。paramN
value :为传入的值
如果参数写了#{id} 是取不到的 因为没有这个key
正确写法这样 但是很麻烦 全是param 不好看
select * from tb1_employee where id =#{param1} and last_name=#{param2}
解决:封装参数时@param(“key”)
public Employee getEmployeemore(@Param("id") Integer id, @Param("lastName")String lastName);
如果不写注解 可以通过传map的方式
总结
第一个场景:public Employee getEmployee (@param(“id”) int id, String name)
取值 id====>#{id/param1} name=#{param2}
第二个场景:public Employee getEmployee (int id, Employee emp)
取值 id====>#{param1} name=#{param2.name}
第二个场景:public Employee getEmployee (List ids)
取值 id====>#{list[0]} 而不是ids【0】 set 数组同理
需求
1如果返回值是一个list 怎么写
配置文件中 resultType=list中存储的类型
2 如果返回值是一个map
resultType写map
3如果返回一个map 而且key是id 或者name 如何制定
在类上写注解 @MapKey(“字段名”)
#{} 以预编译的形式 相当preparedstatement 防止sql注入
${} 取出的值直接赋值 一般不支持占位符的用这个 比如将表名作为参数传递就用这个
上面的参数 mybatis如何实现 下面查看源码
1首先获取 参数 名并封装 names:{0=id ,1=name}
2获取每个标了@param的注解值
3每次解析一个参数给map中保存
key :如果标了注解 就是注解的值
如果没表注解 值就是索引值
4如果只有一个元素 并且没有注解 直接返回args[0]
5如果有多个参数
详细说一下接口的配置文件
namespace:要实现的接口
id:接口中的方法
resultType 返回值类型
parameterType 参数类型
List<User> getById(int id);
<select id="getById" resultType="pojo.User" parameterType="int">
select * from user where id = #{id}
select>
public void Test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao user = sqlSession.getMapper(UserDao.class);
List<User> user1= user.getById(1 );
for (User user2 : user1) {
System.out.println(user2);
}
}
注意点 增删改查需要提交事务
1xml文件中写中文乱码
2标签写错 insert标签写成select标签
3资源文件路径用/ 导入类用点
4配置文件不能出错 报错内容从下网上看
5 增删改不起作用 因为没有提交事务
第一种
<select id="getByLike" resultType="pojo.User">
select * from user where name like #{name}
select>
List<User> users= user.getByLike("%a%");
第二种有sql注入的危险
<select id="getByLike" resultType="pojo.User">
select * from user where name like "%" #{name}"%"
select>
List<User> users= user.getByLike("a");
environment必须有
transactionManager :事务管理器
dataSource
可以快速切换环境
Mybatis配置文件包含了深深影响mybatis行为的设置和属性
Mybatis默认事务管理器是JDBC 连接池是POOLED
我们可以通过properties来实现引用配置文件
在xml文件中 有些属性有顺序 properties被规定必须写在最前面
编写一个properties配置文件
db.properties
driver=com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/test?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
在核心配置文件中引入
<configuration>
<properties resource="properties-config.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
dataSource>
environment>
environments>
<mappers>
<mapper resource="dao/UserMapper.xml"/>
mappers>
configuration>
以上我们将连接数据库的所有参数转移到另外的文件 在引入 完成了优化
必须写在第三个位置 第二个位置是setting
类型别名是为java类型设置的一个短的名字
存在的意义仅在于减少类的完全限定名冗余 别名不区分大小写
方式1
<typeAliases>
<typeAlias type="pojo.User" alias="User">typeAlias>
typeAliases>
接口的配置文件中修改
<select id="getById" resultType="User">
select * from user where id = #{id}select>
select>
方式2 批量起别名 指定包名即可
接口的配置文件中修改
<select id="getUserList" resultType="User">
select * from user
select>
在实体类别少的时候用第一中
如果实体类比较多 用第二种
第一种可以自己定义别名 第二种不行如果有注解则别名为注解值
@Alias(“别名”)直接写到类上 给类起别名
开启驼峰命名
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
mapperRegistry:绑定注册mapper文件
resource:引用类路径下的sql映射文件
url:引用网络路径或者磁盘路径下的sql映射文件
class :引用(注册) 此标签下的xml必须与对应的接口在一个包下且同名
6生命周期和作用域
错误的使用会出现严重的并发问题
sqlsessionfactory:
一旦创建了sqlsessionfactory就被丢弃了 所以是局部变量
sqlsessionfactory:
可以理解为数据库连接池
sqlsessionfactory一旦被创建就会一直运行没有任何理由丢弃或创建一个新的
作用域为整个应用期间
sqlsession
每个线程都有各自的sqlsession
用完赶紧关闭
<resultMap id="userMap" type="User">
<result column="pwd" property="password"/>
resultMap>
<select id="getById" resultMap="userMap">
select * from user where id = #{id}
select>
STDOUT_LOGGING
打错一个字符都不行
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
resource文件下创建properties文件并写入配置信息
log4j.rootLogger = debug,A,B,C
# 输出到控制台
log4j.appender.A = org.apache.log4j.ConsoleAppender
log4j.appender.A.Target = System.out
log4j.appender.A.layout = org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern = %p %t %c - %m%n
# 输出到日志文件
log4j.appender.B = org.apache.log4j.DailyRollingFileAppender
log4j.appender.B.File = logs/log.log
log4j.appender.B.Append = true
log4j.appender.B.Threshold = DEBUG # 输出EBUG级别以上的日志
log4j.appender.B.layout = org.apache.log4j.PatternLayout
log4j.appender.B.layout.ConversionPattern = %p %t %c - %m%n
# 保存异常信息到单独文件
log4j.appender.C = org.apache.log4j.DailyRollingFileAppender
log4j.appender.C.File = logs/error.log # 异常日志文件名
log4j.appender.C.Append = true
log4j.appender.C.Threshold = ERROR #只输出ERROR级别以上的日志
log4j.appender.C.layout = org.apache.log4j.PatternLayout
log4j.appender.C.layout.ConversionPattern = %p %t %c - %m%n
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
4简单使用
1导包
import org.apache.log4j.Logger;
2测试
static Logger logger = Logger.getLogger(UserDaoTest.class);
<select id="getById" resultMap="userMap" resultType="User" parameterType="map">
select * from user limit #{start},#{end}
select>
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map map = new HashMap<String,Integer>();
map.put("start",0);
map.put("end",2);
List<User> list = mapper.getById(map);
for (User user : list) {
System.out.println(user);
}
//关闭
sqlSession.close();
1注解在接口上 2在核心配置文件添加mapper 3测试
public interface UserDao {
@Select("select * from user")
public List<User> getUser();
}
<mappers>
<mapper class="dao.UserDao"/>
mappers>
ublic class UserDaoTest {
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> users = mapper.getUser();
for (User user : users) {
System.out.println(user);
}
//关闭
sqlSession.close();
}
}
当有多个参数的时候
public interface UserDao {
@Select("select * from user where id =#{id} and username = #{username}")
public List<User> getUser(@Param("id") int id,@param("username") String username);
}
如果参数是引用类型就不用写
public interface UserDao {
@Select("insert into user values(#{id},#{name},#{pwd})")
public List<User> getUser(User user);
}
Student类
package pojo;
public class Student {
private int id;
private String name;
private Teacher teacher;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public Student(int id, String name,Teacher teacher) {
this.id = id;
this.name = name;
this.teacher=teacher;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", teacher=" + teacher +
'}';
}
public Student() {
}
}
老师类
package pojo;
public class Teacher {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Teacher() {
}
public Teacher(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "teacher{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
StudentMapper,xml配置文件
<mapper namespace="dao.StudentMapper">
<select id="getStudent" resultMap="aaa">
select * from student
select>
<resultMap id="aaa" type="Student">
<result property="id" column="id" />
<result property="name" column="name"/>
<association property="teacher" column="teacherid" javaType="Teacher" select="getTeacher"/>
resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id =#{teacherid}
select>
mapper>
<select id="getStudent2" resultMap="bbb">
select s.id sid,s.name sname,t.name tname
from student s,teacher t
where s.teacherid=t.id;
select>
<resultMap id="bbb" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
association>
resultMap>
执行结果:
Student{id=1, name=‘saa’, teacher=teacher{id=1, name=‘asa’}}
Student{id=2, name=‘assss’, teacher=teacher{id=1, name=‘asa’}}
Student类
package pojo;
public class Student {
private int id;
private String name;
private int teacherid;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getTeacher() {
return teacherid;
}
public void setTeacher(int teacherid) {
this.teacherid = teacherid;
}
public Student(int id, String name,int teacherid) {
this.id = id;
this.name = name;
this.teacherid=teacherid;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", teacherid=" + teacherid +
'}';
}
public Student() {
}
}
Teacher类
package pojo;
import java.util.List;
public class Teacher {
private int id;
private String name;
List<Student> studentList;
public Teacher() {}
public Teacher(int id, String name, List<Student> studentList) {
this.id = id;
this.name = name;
this.studentList = studentList;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Student> getStudentList() {
return studentList;
}
public void setStudentList(List<Student> studentList) {
this.studentList = studentList;
}
@Override
public String toString() {
return "Teacher{" +
"id=" + id +
", name='" + name + '\'' +
", studentList=" + studentList +
'}';
}
}
package dao;
import org.apache.ibatis.annotations.Select;
import pojo.Teacher;
import java.util.List;
public interface TeacherMapper {
public List<Teacher> getTeacher(int id);
}
<mapper namespace="dao.TeacherMapper">
<select id="getTeacher" resultMap="aaa">
select * from teacher where id = #{tid}
select>
<resultMap id="aaa" type="Teacher">
<collection property="studentList" column="id" javaType="ArrayList" ofType="Student" select="getStudent"/>
resultMap>
<select id="getStudent" resultType="Student">
select * from student where teacherid=#{id}
select>
mapper>
<mapper namespace="dao.TeacherMapper">
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname ,t.id tid,t.name tname
from student s,teacher t
where s.teacherid=t.id and t.id=#{id}
select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="studentList" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="teacherid" column="teacherid"/>
collection>
resultMap>
mapper>
什么是动态sql:根据不同的条件生成不同的sql语句
接口mapper
package dao;
import pojo.Blog;
import java.util.List;
import java.util.Map;
public interface BlogMapper {
public List<Blog> queryByMap(Map map);
}
mapperxml
<mapper namespace="dao.BlogMapper">
<insert id="addBook" parameterType="Blog">
insert into blog values(#{id},#{title},#{author},#{createTime},#{views})
insert>
<select id="queryByMap" parameterType="map" resultType="Blog">
select * from blog where 1=1
<if test="author!=null">
and author = #{author}
if>
<if test="title!=null">
and title=#{title}
if>
select>
mapper>
上面的有问题
如果只查询一个条件就变成了 select * from blog where and author = #{author} where后面直接就是and
这样会报错 要是能自动去掉and就好了
把上述的if语句都放到where标签 如果是第一个就自动去掉and 如果不是就不去掉and 如果没有where条件就全部查询
<select id="queryByMap" parameterType="map" resultType="Blog">
select * from blog
<where>
<if test="author!=null">
and author = #{author}
if>
<if test="title!=null">
and title=#{title}
if>
where>
select>
这个条件只会执行第一次读取到的条件 后面的when不会执行 如果when中的都没有合适的条件就执行other的
<select id="queryByMap" parameterType="map" resultType="Blog">
select * from blog
<where>
<choose>
<when test="title!=null">
and title=#{title}
when>
<when test="views!=null">
and views=#{views}
when>
<otherwise>
and id=1
otherwise>
choose>
where>
select>
会根据情况自动去掉逗号
<update id="updateByMap" parameterType="map">
update blog
<set>
<if test="title!=null">
title=#{title},
if>
<if test="author!=null">
author=#{author},
if>
set>
where id=#{id}
update>
在增删该查过程中有非常多的if语句 我们能将这些重复的提取出来 用的时候引入即可
<mapper namespace="dao.BlogMapper">
<sql id="ifstatment">
<if test="title!=null">
title=#{title},
if>
<if test="author!=null">
author=#{author},
if>
sql>
<update id="updateByMap" parameterType="map">
update blog
<set>
<include refid="ifstatment">include>
set>
where id=#{id}
update>
mapper>
如果我们要拼接一个select *from Blog where (id=1 or id=2 or id=3)这样的语句就这么写
collection=“ids” 代表我们要遍历ids
item=“id” 代表遍历出来的内容放到这里
open=“and (” separator=“or” close=")" 代表拼接
id=#{id} 代表内容
<select id="updateByMap" parameterType="map" resultType="Blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="and (" separator="or" close=")">
id=#{id}
foreach>
where>
select>
动态sql就是拼接sql语句 我们只要保证sql语句正确 排列组合就可以了
什么是缓存 当第一次查询一条数据的时候会进入数据库拿 如果第二次还拿相同的东西就不用去数据库拿 存放到本地
1同一个sqlsession开启到关闭之间的那段代码 一级缓存是默认开启的 不可以关闭
1查询不同的东西 #比如查询1号和2号 用户 他们的缓存是不一样的
2增删改操作 #因为可能改变了数据
3查询不同的Mapper.xml # 使用不同的xml 他们的sqlsession都不一样
4手动清理缓存 #sqlsession.clearCache()
1因为一级缓存作用域太小 所以出现了二级缓存 二级缓存的作用域在整个namespace之间
2工作机制
一个会话查询的一条数据 这个数据就会被放到数据的一级缓存中
如果当前会话关闭了 一级缓存就没了 但我们想 会话关闭后一级缓存内容放入二级缓存中
新的会话查询会查询二级缓存的内容
不同的mapper查出的数据会放在自己对应的缓存(mapper)中
3在mybatis核心配置中显示的开启全局缓存 也就是二级缓存 如果不写出来 默认也是开启的
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
4在要是用二级缓存的mapper.xml中开启二级缓存
也可以写一些参数[
FIFO:先进先出策略