最近复习一下Mybatis框架,简单的记录一下。
Mybatis框架:JDBC的增强
三层架构:界面层(和用户打交道,接收用户请求参数,显示处理结果):controller包(servlet)
业务逻辑层(接收界面传送的数据,计算逻辑,调用数据库,获取数据):service包
持久层(访问数据库,执行对数据增删改查):dao包
三层的交互:
用户使用界面层(springMVC)-->业务逻辑层(service类、spring)--->持久层(Mybatis)--->数据库
动手实现一个小demo:
实现步骤:
1.新建user表
2加入maven的mybatis坐标
3.创建实体类User,
4.创建持久层dao接口,定义操作数据库的方法,userDao
5.创建配置文件(sql映射文件),在接口所在的目录中,文件名称和接口保持一致,userDao.xml
6.创建主配置文件,mybatis-config.xml
提供了数据库连接信息和sql映射文件的位置信息
7.创建使用mybatis的类,通过mybatis访问数据库
首先创建一个项目:
配置好maven,注意添加一下archetypeCatalog=internal,否则可能会报错,出现没有src目录的情况;
首先目录结构是这样的,
1.加入依赖:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
加入mysql驱动:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
2.数据库中新建表user,
3.创建实体类User,
package com.example.entity;
import java.text.SimpleDateFormat;
import java.util.Date;
public class User {
private Integer id;
private String user_name;
private String psd;
private String name;
private Integer sex;
private Integer age;
private Date birthday;
private String created;
private String updated;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUser_name() {
return user_name;
}
public void setUser_name(String user_name) {
this.user_name = user_name;
}
public String getPsd() {
return psd;
}
public void setPsd(String psd) {
this.psd = psd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getCreated() {
return created;
}
public void setCreated(String created) {
this.created = created;
}
public String getUpdated() {
return updated;
}
public void setUpdated(String updated) {
this.updated = updated;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", user_name='" + user_name + '\'' +
", psd='" + psd + '\'' +
", name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", birthday='" + new SimpleDateFormat("yyyy-MM-dd").format(birthday) + '\'' +
", created='" + created + '\'' +
", updated='" + updated + '\'' +
'}';
}
}
4.创建接口UserDao,
package com.example.dao;
import com.example.entity.User;
import java.util.List;
//操作接口
public interface UserDao {
//int表示执行后,影响数据库的行数
public void addUser(User user);
public void deleteUser(User user);
public void updateUser(User user);
public User queryUserById(Integer id);
//查询表中所有数据,返回一个集合,保存User对象
public List<User> queryUserAll();
}
5.创建配置文件(sql映射文件),在接口所在的目录中(或者放在resources目录下mapper目录中),文件名称和接口保持一致,userDao.xml
<?xml version='1.0' encoding='UTF-8'?>
<!--约束文件,限制、检查在当前文件中出现的标签,属性必须符合mybatis要求 -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--当前文件根标签,必须的
namespace:命名空间,唯一值,可以是自定义的字符串,要求使用dao接口的全限定名称
-->
<mapper namespace="com.example.dao.UserDao">
<!--在当前文件中,可以使用特定的标签,表示数据库的特定操作 -->
<!--查询-->
<!-- id要求使用接口中的方法名
resultType表示sql执行后得到的ResultSet,遍历这个结果集得到Java对象的类型,要求是类型的全限定名称 -->
<select id="queryUserAll" resultType="com.example.entity.User">
select * from user ;
</select>
<insert id="addUser" parameterType="com.example.entity.User">
INSERT INTO user (
user_name,
psd,
name ,
age,
sex,
birthday,
created,
updated
)
VALUES
(
#{user_name},
#{psd},
#{name},
#{age},
#{sex},
#{birthday},
now(),
now()
);
</insert>
<delete id="deleteUser">
delete from user where id=#{id};
</delete>
<update id="updateUser" parameterType="com.example.entity.User">
update user
<trim prefix="set" suffixOverrides=",">
<if test="user_name!=null">user_name = #{user_name},</if>
<if test="psd!=null">psd = #{psd},</if>
<if test="name!=null">name = #{name},</if>
<if test="age!=null">age = #{age},</if>
<if test="sex!=null">sex = #{sex},</if>
<if test="birthday!=null">birthday = #{birthday},</if>
updated = now(),
</trim>
where id = #{id};
</update>
</mapper>
6,全局配置文件mybatis-config.xml,放在resources下,
<?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="development">
<environment id="development">
<!--mybatis事务类型,type表示使用jdbc中的Connection对象的commit、rollback做事务处理
-->
<transactionManager type="JDBC"></transactionManager>
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/testdemo" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<!--sql映射文件的位置 -->
<mappers>
<mapper resource="mapper/UserDao.xml"></mapper>
</mappers>
</configuration>
7.编写测试类进行单元测试,其中,mybatis默认不提交事务,需要在insert,update,delete后手动提交事务,否则在数据库中查询不到数据更新,
package com.example.test;
import com.example.entity.User;
import com.example.utils.MybatisUtils;
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;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
public class Test {
/**
* 查询数据
*/
@org.junit.Test
public void queryUserAll() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
//5.【重要】指定要执行的sql语句的标识,sql映射文件中的namespace+"."+标签的id值
String sqlId="com.example.dao.UserDao"+"."+"queryUserAll";
//6.执行sql语句
List<User> userList = sqlSession.selectList(sqlId);
//7.遍历结果
for (User user : userList) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
/**
* 添加数据
*/
@org.junit.Test
public void addUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
//5.【重要】指定要执行的sql语句的标识,sql映射文件中的namespace+"."+标签的id值
String sqlId = "com.example.dao.UserDao" + "." + "addUser";
//执行sql语句
User user = new User();
//user.setId(3);
user.setUser_name("ls");
user.setAge(27);
user.setName("李四");
user.setPsd("222222");
user.setSex(2);
// String time = "1993-12-13";
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// Date date = null;
// date = sdf.parse(time);
user.setBirthday(new Date());
int flag=sqlSession.insert(sqlId,user);
//7.输出结果
System.out.println(user);
//mybatis默认不提交事务,需要在insert,update,delete后手动提交
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
/**
* 修改数据
*/
@org.junit.Test
public void updateUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
//5.【重要】指定要执行的sql语句的标识,sql映射文件中的namespace+"."+标签的id值
String sqlId = "com.example.dao.UserDao" + "." + "updateUser";
//执行sql语句
User user = new User();
user.setName("李");
user.setUser_name("ls");
//user.setAge(26);
user.setSex(2);
user.setId(12);
user.setPsd("111111");
String time = "1993-12-13";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
date = sdf.parse(time);
user.setBirthday(date);
int flag=sqlSession.update(sqlId,user);
//7.输出结果
System.out.println(user);
//mybatis默认不提交事务,需要在insert,update,delete后手动提交
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
/**
* 删除数据
*/
@org.junit.Test
public void deleteUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
//5.【重要】指定要执行的sql语句的标识,sql映射文件中的namespace+"."+标签的id值
String sqlId="com.example.dao.UserDao"+"."+"deleteUser";
//6.执行sql语句
int num= sqlSession.delete(sqlId,12);
System.out.println(num);
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
}
8.其中用到了一个工具类MybatisUtils,和Hibernate中同样原理,构建单态模式下的SqlSessionFactory。首先声明了一个私有的静态类型的SqlSessionFactory对象,来供类中的其他成员使用,接下来通过静态方法构建SqlSessionFactory实例,最后提供了一个公有的静态方法供外部获取SqlSession对象。通过此工具类,在项目中就可以直接通过MybatisUtils.getSqlSession()的方式获取SqlSession对象:
package com.example.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 {
// 1.指定全局配置文件
String resource = "mybatis-config.xml";
// 2.读取配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 3.构建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//4.构建sqlSession,从sqlSessionFactory获取
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
中间遇到报错,说是空指针异常:
因为定义完全局变量sqlSessionFactory之后,又在static中定义了sqlSessionFactory导致getsession中的sqlsession对象为空。
只需要将static对象中的factory类名去掉就行。
最终目录结构:
关于具体的配置讲解,
1、 mybatis配置
mybatis-config.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在mybatis-config.xml中加载。
2、 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
3、 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
4、 mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
5、 Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
6、 Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的Java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
7、 Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
输出映射(返回的结果集可以有哪些类型)
1)基本类型
2)pojo类型
3)List类型
核心对象:
1.SqlSessionFactoryBuilder
用过即丢,其生命周期只存在于方法体内
可重用其来创建多个 SqlSessionFactory 实例
负责构建SqlSessionFactory,并提供多个build方法的重载
2.SqlSessionFactory
SqlSessionFactory是每个MyBatis应用的核心
作用:创建SqlSession实例
作用域:Application
生命周期与应用的生命周期相同
单例模式
存在于整个应用运行时,并且同时只存在一个对象实例
3.SqlSession
包含了执行SQL所需的所有方法
对应一次数据库会话,会话结束必须关闭
线程级别,不能共享,在SqlSession里可以执行多次SQL语句,但一旦关闭了SqlSession就需要重新创建
SqlSession的两种使用方式
01.通过SqlSession实例直接运行映射的SQL语句
02.基于Mapper接口方式操作数据