MyBatis
一、Mybatis 框架介绍
MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
简单概括:
更加简化jdbc代码,简化持久层,sql语句从代码中分离,利用反射,将表中数据与java bean 属性一一映射即ORM(Object Relational Mapping 对象关系映射)
二、Mybatis环境搭建
1.新建Java项目
新建Java项目,导入核心java包与mybatis依赖jar包。
在项目下新建一个lib文件夹,把需要的架包复制到lib文件夹中。选中所有的jar包,右键build path->add to build path 即可。
2.Mybatis配置文件
mybatis提供两种配置文件, 核心配置文件 mybatis-config.xml或mybatis.xml 与 SQL映射文件mapper.xml。
2.1 mybatis核心配置文件的添加
1.在src下新建一个mybatis-config.xml文件。
2.去mybatis的官网(https://mybatis.org/mybatis-3/zh/getting-started.html),拷代码,粘贴复制到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">
<!--mybatis的配置-->
<configuration>
<!--数据源的配置s 可以有多个-->
<environments default="development">
<!--某一个具体的数据源环境-->
<environment id="development">
<!--事务的控制-->
<transactionManager type="JDBC"/>
<!--数据源 连接池-->
<dataSource type="POOLED">
<!--驱动-->
<property name="driver" value="${driver}"/>
<!--url-->
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
</mappers>
</configuration>
3.填入value值,这里以Oracle数据库为例:
<dataSource type="POOLED">
<!--驱动-->
<property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
<!--url-->
<property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
<property name="username" value="SCOTT"/>
<property name="password" value="TIGER"/>
</dataSource>
到这里mybatis核心配置文件就加载好了。
2.2 Mybatis SQL映射文件
1.先要与数据库中的表对应,建立一个对应的类,比如类名为Dept。
2.在这个类的包下,建立一个DeptMapper.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="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
根据实际情况,加以修改:
<?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.shsxt.pojo.DeptMapper">
<!--查询语句,可以先不写-->
<select id="selectDept" resultType="com.shsxt.pojo.Dept">
select * from dept where deptno = #{deptno}
</select>
</mapper>
注意
:不要忘记mybatis核心xml文件中的mapper配置。
3.在mybatis-config.xml中的mappers标签下添加DeptMapper.xml映射文件的路径
<mappers>
<mapper resource="com/shsxt/pojo/DeptMapper.xml"/>
</mappers>
2.3 测试
在另一个包下新建一个测试类:
public class Mybatis001 {
public static void main(String[] args) throws Exception{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//在mybatis中要使用SqlSession的对象来进行
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
System.out.println(sqlSession);
//测试代码
//释放资源
sqlSession.close();
inputStream.close();
}
}
如果输出了sqlsession对象的地址,则mybatis环境搭建成功。
3.进一步优化mybatis环境
3.1 Log4J日志
3.1.1 Log4J简介
日志是应用软件中不可缺少的部分,Apache的开源项目log4j是一个功能强大的日志组件,提供方便的日志记录。在apache网站:jakarta.apache.org/log4j可以免费下载到Log4j最新版本的软件包。
3.1.2 日志级别
分为五个级别:
DEBUG(人为调试信息)
、INFO(普通信息)
、WARN(警告)
、ERROR(错误)
、FATAL(系统错误)
这五个级别是有顺序的,DEBUG < INFO < WARN < ERROR < FATAL,分别用来指定这条日志信息的重要程度,明白这一点很重要。
Log4j有一个规则:只输出级别不低于设定级别的日志信息,假设Loggers级别设定为INFO,则INFO、WARN、ERROR和FATAL级别的日志信息都会输出,而级别比INFO低的DEBUG则不会输出。
3.1.3 Log4J使用
1.导包,上面已经导过了。
2.在src下新建一个 log4j.properties文件。
3.把下面的代码考入其中。
# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=INFO, CONSOLE
# log4j.rootCategory=INFO, CONSOLE, LOGFILE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=- %m %c %l %d{yyyy-MMddHH:mm:ss}%n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=e:/test.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=- %m %l%n
一个是在控制台(CONSOLE)输出,一个是在磁盘输出(LOGFILE)。你自己可以选择输出位置。
3.1.4 常见的日志输出格式
3.1.5 通过settings标签开启 log4j 的支持
settings用于设置 MyBatis 在运行时的行为方式, 例如:缓存, 延迟加载, 日志等。
mybatis-config.xml核心配置文件下面的子目录必须按照以下顺序写:
(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)"。
<!-- settings标签 -->
<settings>
<!-- 设置MyBatis使用log4j日志支持 -->
<setting name="logImpl" value="LOG4J"/>
</settings>
3.2 通过properties标签实现软编码
3.2.1 src下定义配置文件db.properties
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:XE
username=SCOTT
password=TIGER
3.2.2 properties标签
mybatis核心配置文件中添加properties标签,指定加载外部的properties文件,注意定义位置。
<!-- 加载外部的properties文件 -->
<properties resource="db.properties"/>
3.2.3 使用方式
获取properties文件中数据时候,要通过${}的方式获取。
<dataSource type="POOLED">
<!--驱动-->
<property name="driver" value="${driver}"/>
<!--url-->
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
注意
:这里的value值要和db.properties里的值保持一致
3.3 typeAliases标签
用于给java类型定义别名,方便在配置文件中使用。
3.3.1 给某一个类定义别名
注意typeAliases标签在核心配置文件下的位置。
<typeAliases><
typeAliastype="com.shsxt.pojo.User"alias="u"/>
</typeAliases>
3.3.2 省略alias属性, 表示类别名为类名
<typeAliases>
<typeAliastype="com.shsxt.pojo.User"/> alias属性不写,默认类名,不区分大小写
</typeAliases>
3.3.3可以通过package标签给整个包下的所有类定义别名
别名为类名。
<typeAliases>
<package name="com.shsxt.pojo"/><!-- 包下所有的类默认类名 -->
</typeAliases>
别名配置好之后,我们的映射文件DeptMapper.xml下的查询语句的返回值类型,直接写类名就行。
<!--之前的写法-->
<select id="selectDept" resultType="com.shsxt.pojo.Dept">
select * from dept where deptno = #{deptno}
</select>
<!--用过别名之后的写法-->
<select id="selectDept" resultType="Dept">
select * from dept where deptno= 20
</select>
3.3.4 Mybatis的内建别名
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
三、MyBatis简单使用
1.MyBatis的三个查询方法
selectOne
(“命名空间.id”) 用于查询单条数据,返回一个数据, 如果没有查到返回null。
DeptMapper.xml映射文件下:
<!--查询部门编号为20的部门信息-->
<select id="selectDept" resultType="Dept">//resultType是返回值类型,后面会详解
select * from dept where deptno= 20
</select>
测试类下:
Dept dept = sqlSession.selectOne("om.shsxt.pojo.DeptMapper.selectDept");
System.out.println(dept);
selectList
(“命名空间.id”) 用户查询多条数据情况,返回一个List集合, 没有查到数据返回空集合,不是null。
DeptMapper.xml映射文件下:
<!--查询所有的部门信息 集合,存放的就一个一个的Dept对象-->
<select id="selectDeptAll" resultType="Dept">
select * from dept
</select>
测试类下:
List<Dept> depts = sqlSession.selectList("com.shsxt.pojo.DeptMapper.selectDeptAll");
depts.forEach(System.out::println);//方法引用
selectMap
(“命名空间.id”,key的字段名) 用于查询多条记录情况, 返回Map集合, 需要指定那个属性作为key, sql查询结果作为value,指定的字段值作为key, 如果查不到, 返回一个空map集合,不是null
DeptMapper.xml映射文件下:
<!--查询所有的部门信息 集合,存放的就一个一个的Dept对象-->
<select id="selectDeptMap" resultType="Dept">
select * from dept
</select>
测试类下:
/**
* 查询所有的部门信息 集合,存放的就一个一个的Dept对象 selectMap 无序
*/
Map<Integer, Dept> depts = sqlSession.selectMap("com.shsxt.pojo.DeptMapper.selectDeptMap", "deptno");
depts.forEach((t,u)->{
System.out.println(t + "--" + u);
});
2.parameterType参数类型
如果执行的是条件查询,DML,需要在调用方法的时候传递参数,此时, 可以在sql标签中通过parameterType属性指定参数的类型(别名|权限定名). 而在sql语句,通过#{}的方式获取参数。
2.1 一个参数的查询
DeptMapper.xml映射文件下:
<!--根据部门编号查询 部门信息-->
<select id="selectDeptByNo" resultType="Dept" parameterType="int">
select * from dept where deptno=#{deptno}<!-- 可以任意填写 -->
</select>
测试类下:
Dept dept = sqlSession.selectOne("com.shsxt.pojo.DeptMapper.selectDeptByNo", 10);
System.out.println(dept);
2.2 多个参数查询
多个参数传递时, 由于sqlSession中提供的查询方法,只允许传递一个sql参数, 因此可以对多个参数进行封装,可以对象,集合,数组…
2.2.1 封装成一个类
DeptMapper.xml映射文件下:
<!-- 查询10部门和 SALES 部门的信息-->
<select id="selectByA" resultType="Dept" parameterType="Dept">
/*参数是对象, 必须要指定固定的属性 名*/
select * from dept where deptno=#{deptno} or dname=#{dname}
</select>
测试类下:
Dept dept = new Dept();
dept.setDeptno(10);
dept.setDname("SALES");
List<Dept> depts = sqlSession.selectList("com.shsxt.pojo.DeptMapper.selectByA", dept);
depts.forEach(System.out::println);
2.2.2 参数类型是一个map
DeptMapper.xml映射文件下:
<!--查询 10部门和20部门的部门信息-->
<select id="selectByB" resultType="Dept" parameterType="map">
select * from dept where deptno=#{dept1} or deptno=#{dept2}
</select>
测试类下:
Map<String,Integer> map = new HashMap<>();
map.put("dept1",10);
map.put("dept2",20);
List<Dept> depts = sqlSession.selectList("com.shsxt.pojo.DeptMapper.selectByB", map);
depts.forEach(System.out::println);
2.2.3 数组|List
数组:
DeptMapper.xml映射文件下:
<!--查询10部门和20部门还有30部门的部门信息 继续用map也可以-->
<select id="selectByC" resultType="Dept" >
select * from dept where deptno in
/*遍历一个array集合, 每一个当前的元素都放入item-> e 在遍历开始 ( 结束 ) 每一个元素之间用 每一个元素之间用,分割*/
<foreach collection="array" item="e" open="(" close=")" separator=",">
#{e}
</foreach>
</select>
测试类下:
Integer[] arr = {10,20,30};
List<Dept> depts = sqlSession.selectList("com.shsxt.pojo.DeptMapper.selectByC", arr);
depts.forEach(System.out::println);
list:
DeptMapper.xml映射文件下:
<select id="selectByD" resultType="Dept" parameterType="list">
select * from dept where deptno in
/*遍历一个array集合, 每一个当前的元素都放入item-> e 在遍历开始 ( 结束 ) 每一个元素之间用 每一个元素之间用,分割*/
<foreach collection="list" item="e" open="(" close=")" separator=",">
#{e}
</foreach>
</select>
测试类下:
Integer[] arr = {10,20,30};
List<Integer> list = Arrays.asList(arr);
List<Dept> depts = sqlSession.selectList("com.shsxt.pojo.DeptMapper.selectByD", list);
depts.forEach(System.out::println);
3.resultType返回值类型
基本数据类型(包装类) 、 String 、Date 、JavaBean 、List Map 、List-Map。
3.1 Date类型
EmpMapper.xml映射文件下:
<!--根据员工编号查询员工的入职日期-->
<select id="selectByA" resultType="Date" parameterType="int">
select hiredate from emp where empno=#{empno}
</select>
测试类下:
Date hiredate = sqlSession.selectOne("com.shsxt.pojo.EmpMapper.selectByA", 7369);
System.out.println(new SimpleDateFormat().format(hiredate));//80-12-17 上午12:00
3.2 List类型
EmpMapper.xml映射文件下:
<!--找到7698这个领导的手下 返回多个员工 list-->
<select id="selectByB" resultType="Emp" >
select * from emp where mgr=#{mgr}
</select>
测试类下:
List<Emp> emps = sqlSession.selectList("com.shsxt.pojo.EmpMapper.selectByB", 7698);
emps.forEach(System.out::println);
EmpMapper.xml映射文件下:
<!--找到名字中有A的员工信息-->
<select id="selectByC" resultType="Emp" parameterType="string">
select * from emp where ename like '%'||#{ename}||'%'
</select>
测试类下:
List<Emp> emps = sqlSession.selectList("com.shsxt.pojo.EmpMapper.selectByC", "A");
emps.forEach(System.out::println);
3.3 Map类型
EmpMapper.xml映射文件下:
<!--查询的数据不存放到 Emp中, 利用一个Map集合来存储 Emp的属性名作为 key, 属性值 vlue-->
<!--根据员工编号,查询员工信息-->
<select id="selectByD" resultType="map">
select * from emp where empno=#{empno}
</select>
测试类下:
Map<String,Object> map = sqlSession.selectOne("com.shsxt.pojo.EmpMapper.selectByD", 7369);
map.forEach((t,u)->{
System.out.println(t + "--" + u);
});
3.4 List-Map类型
EmpMapper.xml映射文件下:
<!--查询那些没有提成的人的信息 Map中-->
<select id="selectByE" resultType="map">
select * from emp where comm is null
</select>
测试类下:
List<Map<String,Object>> empMapList = sqlSession.selectList("com.shsxt.pojo.EmpMapper.selectByE");
// empMapList.forEach(emp->emp.entrySet().forEach(entry-> System.out.println(entry.getKey() + "-" + entry.getValue())));
empMapList.forEach((t)->{
t.forEach((x,y)->{
System.out.println(x + "--" + y);
});
});
四、MyBatis的其他操作
1.工具类的封装
/**
* 工具类: 封装获取sqlSession的固定操作,释放资源的固定操作
*/
public class MyBatisUtils {
// 私有是因为外界不需要知道这个对象的存在, static是因为只要一个就够了
private static SqlSessionFactory factory = null;
// 静态代码块也只会执行一次,而且就是加载类的时候执行
static {
try {
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
// 对外提供一个公开的,静态方法,让别人获取sqlSession
public static SqlSession getSqlSession(){
return factory.openSession();//手动提交
}
public static SqlSession getSqlSession(boolean flag){
return factory.openSession(flag);//自动提交
}
//释放资源
public static void close(SqlSession sqlSession){
if(sqlSession!=null){
sqlSession.close();
}
}
}
封装好之后,在测试类只需要写:
//获取sqlSession
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//释放资源
MyBatisUtils.close(sqlSession);
2.增删改操作
2.1 insert
DeptMapper.xml映射文件下:
<!--向dept表中添加一条记录-->
<insert id="insertByA" parameterType="Dept">
insert into dept(deptno,dname,loc) values (#{deptno}, #{dname}, #{loc})
</insert>
测试类下:
Dept dept = new Dept(60,"SHSXT","SHANGHAI");
// 返回值是这一次操作所影响的行数
int row = sqlSession.insert("com.shsxt.pojo.DeptMapper.insertByA", dept);
if(row<=0){
System.out.println("插入失败");
}else {
System.out.println("插入成功");
}
2.2 update
DeptMapper.xml映射文件下:
<!--修改dept表中的数据 把deptno=60 LOC改成 PUDONG-->
<update id="updateDept" parameterType="Dept">
update dept set loc = #{loc} where deptno=#{deptno}
</update>
测试类下:
Dept dept = new Dept();
dept.setDeptno(60);
dept.setLoc("PUDONG");
int row = sqlSession.update("com.shsxt.pojo.DeptMapper.updateDept", dept);
if(row<=0){
System.out.println("更新失败");
}else {
System.out.println("更新成功");
}
2.3 delete
DeptMapper.xml映射文件下:
<!--删除dept表中的数据 60-->
<delete id="deleteDept" >
delete from dept where deptno=#{id}
</delete>
测试类下:
int row = sqlSession.delete("com.shsxt.pojo.DeptMapper.deleteDept", 60);
if(row<=0){
System.out.println("删除失败");
}else {
System.out.println("删除成功");
}