Mybatis入门案例
搭建环境
创建表
CREATE TABLE `users` ( `userid` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(20) DEFAULT NULL, `usersex` varchar(10) DEFAULT NULL, PRIMARY KEY (`userid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
添加DTD文件
添加DTD约束文件
在没有联网的情况下,如果让 dtd 约束继续起作用,并且出现标签提示,可以通过引入 本地 dtd 文件来实现。
下 载 dtd : 在 浏 览 器 中 输 入 dtd 的 网 络 地 址 即 可 实 现 下 载 。 比 如 : http://mybatis.org/dtd/mybatis-3-config.dtd
将下载的 dtd 拷贝到本地的一个目录下
Idea 操作路径:File—Settings—Languages & Frameworks
其中 URL 复制 dtd 的网络地址即可。File 选择 dtd 文件在本地的地址。OK!
注:在 MyBatis 的核心 jar 包中就提供了 mybatis-3-config.dtd
添加映射配置DTD文件
其中 URL 复制映射文件的网络地址即可。File 选择 映射文件在本地的地址。OK!
创建项目
创建一个java project的项目即可
添加jar包
一共需要添加12个jar包
一个mybatis的核心jar包,位置在下载的mybatis文件夹内:
在lib文件夹下,有10个辅助jar包:
同时还需要一个数据库的驱动jar包:
我们将其整合到一个文件夹下:
一共12个jar包
将其导入到项目内:
创建实体
package com.bjsxt.pojo;
public class Users {
private int userid;
private String username;
private String usersex;
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUsersex() {
return usersex;
}
public void setUsersex(String usersex) {
this.usersex = usersex;
}
}
创建 properties 文件
将其放到src的根目录下
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3307/sys
jdbc.username=root
jdbc.password=1234567
创建全局配置文件
将其放到src的根目录下
<?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>
<!--引入properties文件-->
<properties resource="db.properties"/>
<!-- 开启SLF4J做日志处理-->
<settings>
<setting name="logImpl" value="SLF4J"/>
</settings>
<!--配置别名-->
<typeAliases>
<typeAlias type="com.bjsxt.pojo.Users" alias="u"/>
<package name="com.bjsxt.pojo"/>
</typeAliases>
<!--环境的配置-->
<environments default="development">
<environment id="development">
<!-- 配置事务-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入映射配置文件-->
<mappers>
<!-- 使用相对路径方式引入-->
<mapper resource="com/bjsxt/mapper/UsersMapper.xml"/>
</mappers>
</configuration>
我们需要创建一个映射配置文件,并将其引入到全局配置文件内
关键代码为:
<!--引入映射配置文件-->
<mappers>
<!-- 使用相对路径方式将映射配置文件引入-->
<mapper resource="com/bjsxt/mapper/UsersMapper.xml"/>
</mappers>
创建映射配置文件
创建的映射配置文件的名字和实体类有关
<?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.bjsxt.mapper.UserMapper">
</mapper>
查询所有数据
修改映射配置文件UsersMapper.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.bjsxt.mapper.UserMapper">
<!--查询所有用户-->
<select id="selectUsersAll" resultType="com.bjsxt.pojo.Users">
select * from users
</select>
</mapper>
创建UserDao接口
package com.bjsxt.dao;
import com.bjsxt.pojo.Users;
import java.io.IOException;
import java.util.List;
public interface UsersDao {
List<Users> selectUsersAll() throws IOException;
}
创建UserDao接口实现类
package com.bjsxt.dao.impl;
import com.bjsxt.dao.UsersDao;
import com.bjsxt.pojo.Users;
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.util.List;
public class UsersDaoImpl implements UsersDao {
@Override
public List<Users> selectUsersAll()throws IOException {
//创建SqlSessionFactory对象
InputStream inputStream = Resources.getResourceAsStream("mybatis-cfg.xml");
SqlSessionFactory sqlSessionFacotry = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象
SqlSession sqlSession = sqlSessionFacotry.openSession();
//通过SqlSession对象下的API完成对数据库的操作
List<Users> list = sqlSession.selectList("com.bjsxt.mapper.UserMapper.selectUsersAll");
//关闭SqlSession对象
sqlSession.close();
return list;
}
}
创建测试类
package com.bjsxt.test;
import com.bjsxt.dao.UsersDao;
import com.bjsxt.dao.impl.UsersDaoImpl;
import com.bjsxt.pojo.Users;
import java.io.IOException;
import java.util.List;
public class Test {
public static void main(String[] args) throws IOException {
UsersDao usersDao = new UsersDaoImpl();
List<Users> list = usersDao.selectUsersAll();
for(Users users:list){
System.out.println(users.getUserid() +"\t"+users.getUsername()+"\t "+users.getUsersex());
}
}
}
注意:
-
当控制台爆出如下异常:
这是因为没有在db.properties文件内jdbc.url后添加?useSSL=false
添加后异常消失 -
如果爆出如下异常:
也就是java.sql.SQLException: Unknown error 1045
这是密码jdbc.password前后有空格导致的。
去除后正常 -
出现如下问题
说明没有添加log4j的输出日志
创建一个log4j的输出日志文件
log4j.rootLogger=debug,console,logfile
### appender.console输出到控制台 ###
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=<%d> %5p (%F:%L) [%t] (%c) - %m%n
log4j.appender.console.Target=System.out
### appender.logfile输出到日志文件 ###
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=SysLog.log
log4j.appender.logfile.MaxFileSize=500KB
log4j.appender.logfile.MaxBackupIndex=7
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=<%d> %p (%F:%L) [%t] %c - %m%n
异常消失
根据用户的id去查询数据
修改映射配置文件UsersMapper.xml
<!--根据用户ID查询用户-->
<select id="selectUsersById" parameterType="int" resultType="users">
select * from users where userid = #{suibian}
</select>
修改UsersDao接口
package com.bjsxt.dao;
import com.bjsxt.pojo.Users;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public interface UsersDao {
List<Users> selectUsersAll()throws IOException;
//添加根据id查询用户的抽象方法
Users selectUsersById(int userid)throws IOException;
}
修改 UsersDao 接口实现类
/**
* 根据用户ID查询用户
* @param userid
* @return
* @throws IOException
*/
@Override
public Users selectUsersById(int userid) throws IOException {
//创建SqlSessionFactory对象
InputStream inputStream = Resources.getResourceAsStream("mybatis-cfg.xml");
SqlSessionFactory sqlSessionFacotry = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象
SqlSession sqlSession = sqlSessionFacotry.openSession();
Users users = sqlSession.selectOne("com.bjsxt.mapper.UserMapper.selectUsersById",userid);
sqlSession.close();
return users;
}
修改测试类
package test;
import com.bjsxt.dao.UsersDao;
import com.bjsxt.dao.impl.UsersDaoImpl;
import com.bjsxt.pojo.Users;
import java.io.IOException;
public class Test {
public static void main(String[] args)throws IOException {
UsersDao usersDao = new UsersDaoImpl();
Users users = usersDao.selectUsersById(1);
System.out.println(users.getUserid()+"\t"+users.getUsername()+"\t"+users.getUsersex());
}
}
Mybatis中的参数绑定的方式
在映射配置文件中向 SQL 语句中绑定参数的语法结构为#{ }和${ }。
#{ } 和 ${ }的区别:
#{ } 解析为一个 JDBC 预编译语句(PreparedStatement)的参数标记符占位符 ?。使 用该方式可避免 SQL 注入。
仅
仅
为
一
个
纯
碎
的
S
t
r
i
n
g
替
换
,
在
M
y
b
a
t
i
s
的
动
态
S
Q
L
解
析
阶
段
将
会
进
行
变
量
替
换
。
{ } 仅仅为一个纯碎的 String 替换,在 Mybatis 的动态 SQL 解析阶段将会进行变量替 换。
仅仅为一个纯碎的String替换,在Mybatis的动态SQL解析阶段将会进行变量替换。{ } 在预编译之前已经被变量替换了,这会存在 SQL 注入问题。
创建 Mybatis 的工具类
为什么要使用Mybatis的工具类
因为对比上面的接口实现类内的两个查询方法,我们可以发现代码可以被进一步简化
用到的知识
ThreadLocal介绍
ThreadLocal提供了线程内存储变量的能力,这些变量的不同之处在于每一个线程读取的变量是对应的,相互独立的。通过get和set方法就可以得到当前线程对应的值。
使用ThreadLocal去存储SqlSession
如果多个DML操作属于一个事务,因为commit()和rollback()都是由SqlSession完成,所以必须保证使用一个SQLSession。但是多个不同的DML操作可能在不同类的不同方法内,每个方法中要单独的获取SqlSession。比如商城下订单时,其实涉及商品库存变化、订单添加、订单明细添加、付款、日志添加等多个 DML 操作,分布在不同类中。
如何在多个 DML 操作之间使用同一个 SqlSession 呢,可以使用 ThreadLocal 来存储。保 证一个线程中的操作使用的都是一个 SqlSession。
创建Mybatis工具类
package com.bjsxt.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 ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();
private static SqlSessionFactory sqlSessionFactory = null;
static{
//创建SqlSessionFactory
InputStream is = null;
try{
//读取mybatis-cfg.xml文件
is = Resources.getResourceAsStream("mybatis-cfg.xml");
}catch (IOException e){
e.printStackTrace();
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
}
//获取SqlSession
public static SqlSession getSqlSession(){
SqlSession sqlSession = threadLocal.get();
//在当前线程内没有找到的时候(也就是第一次通过工具类在sqlsession内查找没有找到)
if(sqlSession == null){
sqlSession = sqlSessionFactory.openSession();
threadLocal.set(sqlSession);
}
return sqlSession;
}
//关闭SqlSession
public static void closeSqlSession(){
SqlSession sqlSession = threadLocal.get();
//如果找到
if(sqlSession != null){
sqlSession.close();
threadLocal.set(null);
}
}
}
有这个工具类,mybatis框架不需要被频繁启动;第二个是通过threadLocal缓存了SqlSession ,可以保证再一个线程内对数据库的操作可以拿到一样的SqlSession 。
完成DML操作
Mybatis的事务提交方式
在 Mybatis 中事务提交方式默认为手动提交,这与 JDBC 是不同的。在 JDBC 中事务默认 提交方式为自动提交。
主流是对事务进行手动提交
添加用户操作
修改映射配置文件
<!--添加用户-->
<insert id="insertUsers" >
insert into users value(default ,#{username},#{usersex})
</insert>
修改UsersDao接口
//添加用户的抽象方法
void insertUsers(Users users);
修改UsersDao接口实现类
@Override
public void insertUsers(Users users) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
sqlSession.insert("com.bjsxt.mapper.UserMapper.insertUsers", users);
}
创建业务层的接口UsersService
package com.bjsxt.service;
import com.bjsxt.pojo.Users;
//业务层的抽象方法
public interface UsersService {
//添加的抽象方法
void addUsers(Users users);
}
创建业务层的接口实现类
package com.bjsxt.service.impl;
import com.bjsxt.dao.UsersDao;
import com.bjsxt.dao.impl.UsersDaoImpl;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import com.bjsxt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.Map;
public class UsersServiceImpl implements UsersService {
/**
* 添加用户
* @param users
*/
@Override
public void addUsers(Users users) {
//使用自定义的mybatis抽象类
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
UsersDao usersDao = new UsersDaoImpl();
usersDao.insertUsers(users);
//启动sqlsession
sqlSession.commit();
}catch (Exception e){
e.printStackTrace();
sqlSession.rollback();
}finally{
//使用工具类关闭sqlsession
MybatisUtils.closeSqlSession();
}
}
}
创建新的测试类AddUsersTest
package test;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import com.bjsxt.service.impl.UsersServiceImpl;
public class AddUsersTest {
public static void main(String[]args){
UsersService usersService = new UsersServiceImpl();
Users users = new Users();
users.setUsername("zhangsan");
users.setUsersex("womale");
usersService.addUsers(users);
}
}
也可以使用Scanner将其改为控制台输入
案例:
public static void ScannerTest(){
Scanner sc = new Scanner(System.in);
System.out.println("ScannerTest, Please Enter Name:");
String name = sc.nextLine(); //读取字符串型输入
System.out.println("ScannerTest, Please Enter Age:");
int age = sc.nextInt(); //读取整型输入
System.out.println("ScannerTest, Please Enter Salary:");
float salary = sc.nextFloat(); //读取float型输入
System.out.println("Your Information is as below:");
System.out.println("Name:" + name +"\n" + "Age:"+age + "\n"+"Salary:"+salary);
}
更新用户操作
有两个行为,先查到用户的数据,再将修改后的内容存放在数据表数据原先的位置
修改映射配置文件
<!--预更新用户的查询-->
<select id="selectUserById2" resultType="com.bjsxt.pojo.Users">
select * from users where userid = ${userid}
</select>
<!--更新用户操作-->
<update id="updateUsersById" >
update users set username = #{username},usersex=#{usersex} where userid = #{userid}
</update>
修改 UsersDao 接口
//预更新用户的抽象方法
Users selectUsersById2(int userid);
//更新用户的抽象方法
void updateUsersById(Users users);
修改UsersDao的接口实现类
//预更新用户的查询
@Override
public Users selectUsersById2(int userid) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
Users users = sqlSession.selectOne("com.bjsxt.mapper.UserMapper.selectUserById2",userid);
return users;
}
//更新用户
@Override
public void updateUsersById(Users users) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
sqlSession.update("com.bjsxt.mapper.UserMapper.updateUsersById", users);
}
修改UsersService接口
//获取预更新的用户信息的抽象方法
Users preUpdateUsers(int userid);
//更新的抽象方法
void modifyUsers(Users users);
修改UsersService接口实现类
/**
* 获取预更新用户的信息
* @param userid
* @return
*/
@Override
public Users preUpdateUsers(int userid) {
Users users = null;
try{
SqlSession sqlSession = MybatisUtils.getSqlSession();
UsersDao usersDao = new UsersDaoImpl();
users = usersDao.selectUsersById2(userid);
}catch(Exception e){
e.printStackTrace();
}finally {
MybatisUtils.closeSqlSession();
}
return users;
}
/**
* 更新用户的信息
* @param users
*/
@Override
public void modifyUsers(Users users) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
UsersDao usersDao = new UsersDaoImpl();
usersDao.updateUsersById(users);
sqlSession.commit();
}catch(Exception e){
e.printStackTrace();
//将异常回滚
sqlSession.rollback();
}finally {
MybatisUtils.closeSqlSession();
}
}
创建测试类
package test;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import com.bjsxt.service.impl.UsersServiceImpl;
public class UpdateUsersTest {
public static void main(String[] args) {
UsersService usersService = new UsersServiceImpl();
Users users = usersService.preUpdateUsers(2);
System.out.println("更新前:"+users.getUserid() +" "+users.getUsername()+" "+users.getUsersex());
users.setUsername("OLDLU");
users.setUsersex("MALE");
usersService.modifyUsers(users);
Users users2 = usersService.preUpdateUsers(2);
System.out.println("更新后:"+users2.getUserid() +" "+users2.getUsername()+" "+users2.getUsersex());
}
}
mybatis框架只启动了一次, 我们获取了两次SqlSession,是在主线程内先关闭了一次sqlsession,又在主线程内拿了一次sqlsession,又将这两个sqlsession都放到了同一个threadLocal内
删除用户操作
修改映射配置文件
修改UsersDao接口
<!-- 根据用户ID删除用户-->
<delete id="deleteUsersById">
delete from users where userid = #{userid}
</delete>
修改UsersDaoImpl实现类
@Override
public void deleteUsersById(int userid) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
final int delete = sqlSession.delete("com.bjsxt.mapper.UserMapper.deleteUsersById", userid);
}
修改UsersServlet接口
//根据id删除的抽象方法
void dropUsersById(int userid);
修改UsersServletImpl接口实现类
@Override
public void dropUsersById(int userid) {
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
UsersDao usersDao = new UsersDaoImpl();
usersDao.deleteUsersById(userid);
sqlSession.commit();
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
}finally{
MybatisUtils.closeSqlSession();
}
}
创建测试类
package com.bjsxt.test;
import com.bjsxt.service.UsersService;
import com.bjsxt.service.impl.UsersServiceImpl;
public class DeleteUsersTest {
public static void main(String[] args) {
UsersService usersService = new UsersServiceImpl();
usersService.dropUsersById(3);
}
}