数据持久层框架Mybatis
目录
1.软件开发3层架构
软件开发的3层架构是Java提出分层开发(经典开发)模式,即将软件开发分为表示层、业务逻辑层、数据持久层开发,互相独立,各不影响。
表示层:
- 由与用户进行交互的组件和容器构成(接收请求并响应请求),比如servlet。
- 分包:action/web/servlet。
- 处理表示层的框架SprintMVC/Struts2。
业务逻辑层:
- 由业务处理的组件组成。
- 分包:service/biz。
- 处理业务逻辑层的框架Spring Framwork。
数据持久层:
- 实现与数据库直接交互,比如jdbc。
- 分包:dao。
- 处理数据持久层的框架Mybatis/Hibernate/JdbcTemplate。
2.什么是框架?
定义:
- 将重复性的、繁琐的内容封装起来的一套程序。
- 框架使开发人员能够将更多地精力放在业务的分析和理解上。
好处:
- 简化开发,配置即可
- 屏蔽细节
- 提高开发效率
3.传统JDBC开发的不足?
在引入框架之前,我们都使用JDBC完成数据持久层的开发,但有下面几处不足:
- 数据库驱动注册和连接获取的硬编码问题(配置文件解决)
- SQL预编译时赋值和结果集封装的时候繁琐(反射解决 ORM,即对象关系映射)
- 频繁打开和释放连接消耗资源(连接池解决)
4.什么是Mybatis?
官网:http://www.mybatis.org/mybatis-3
- MyBatis 是一款优秀的持久层框架
- MyBatis只需要关注SQL语句、输入参数和输出结果的映射
- MyBatis是一个ORM框架。ORM表示对象关系映射,解决面向对象编程模型和关系型数据库模型之间的映射问题。
5.Mybatis入门案例?
步骤:
- 准备jar包,mysql驱动包(mysql-connector-java-5.1.45-bin.jar)和Mybatis核心jar包(mybatis-3.4.5.jar)
- Mybatis原本是apache项目,原来被叫做 ibatis
- 后来移植到google code,改名为mybatis
- 现在在github(开源软件托管平台)脱光,官网:https://github.com/mybatis/mybatis-3/releases
- 创建Java项目,并将前面的2个jar包导入项目,如下图所示,其中lib目录手动创建,2个jar拷贝到lib,并build path二者。
- 创建并编写2种配置文件
- 全局配置文件:4个参数配置连接池、进行事务管理
- 映射配置:定制化sql语句、输入参数与输出结果映射
- 配置方式:
- 在Java项目下创建source folder(与src目录同级并且一样的目录)用于存放配置文件
- 全局配置文件和映射配置文件的编写方式见下方代码
- 读取配置并运行测试
- Sqlsession:面向开发者的一个接口,提供了发送sql命令的方法。
- SqlSessionFactory:session工厂,创建和管理session对象的。
代码:
项目目录结构
pojo中User类
package pojo;
public class User {
private int uid;
private String uname;
private String password;
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [uid=" + uid + ", " + (uname != null ? "uname=" + uname + ", " : "") + (password != null ? "password=" + password : "") + "]";
}
}
Java属性文件jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///bd1808?useSSL=true
user=root
password=root
全局配置文件mybatis.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 这段主要作用就是约束当前文档怎么去写
xml描述文件:dtd/schema,作用是约束标签
-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 加载外部属性文件 -->
<properties resource="jdbc.properties"></properties>
<!-- 类型别名:标签时候顺序的,这个必须写到properties标签的后面 -->
<typeAliases>
<!-- 单个类型别名 -->
<!-- <typeAlias alias="user" type="pojo.User"/> -->
<!-- 批量起别名:别名就是类名,而且不区分大小写 -->
<package name="pojo" />
</typeAliases>
<!-- 环境context:复数
mysql、oracle、db2
本地环境、预上线环境、线上环境
default:默认使用环境
-->
<environments default="development">
<!-- 环境:id是唯一表示 -->
<environment id="development">
<!-- 事务管理器:JDBC,mybatis框架的事务管理器 -->
<transactionManager type="JDBC"/>
<!-- 数据源:POOLED连接池(存储连接) 其他连接池:dbcp/c3p0/druid/jdbctemplate -->
<dataSource type="POOLED">
<!-- name属性的值都是固定的,必须这么写,否则框架无法识别 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 映射器 :配置映射文件路径-->
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
映射配置文件UserMapper.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">
<!-- namespace:命名空间(字符串)
隔离sql
这里abcd随便写的
-->
<mapper namespace="ahsb1">
<!--
根据uid查询user的对象
一个标签就代表一个Statement的对象
#{}:代表占位符,在jdbc里面就是?的意思
#{}中的id:关键字,即输入参数
参数是简单类型:比如基本数据类型和字符串,可任意
参数是用类型:属性名称
id:标签的唯一表示
parameterType:输入参数类型(可选),不写就会进行类型推断
resultType: 输出结果类型(单个结果的类型)
-->
<select id="selectUser" resultType="User">
select * from user where uid = #{uid}
</select>
<!-- 查询所有 -->
<select id="selectAll" resultType="User">
select * from user
</select>
<!-- 模糊查询
${}:表示连接
关键字与输入参数的类型有关:
简单类型:value
引用类型:属性名称
-->
<select id="selectLikeName" resultType="User">
select * from user where uname like '%${value}%'
</select>
<!-- 插入 -->
<insert id="insert1" >
insert into user(uname,password) values(#{uname},#{password})
</insert>
</mapper>
测试类Test
package test;
import java.io.IOException;
import java.io.Reader;
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 pojo.User;
public class Test {
public static void main(String[] args) throws IOException {
//读取配置文件
Reader reader = Resources.getResourceAsReader("mybatis.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
//发送定制好的sql
//SqlSession session = ssf.openSession();//关闭自动提交
SqlSession session = ssf.openSession(true);//不关闭自动提交
/*User user = session.selectOne("ahsb.selectUser", 1);
System.out.println(user);*/
//查询所有
/*List<User> users = session.selectList("ahsb.selectAll");
System.out.println(users);*/
//模糊查询
/*List<User> users = session.selectList("ahsb.selectLikeName","s");
System.out.println(users);*/
User user = new User();
user.setUname("zl");
user.setPassword("123");
int rows = session.insert("ahsb1.insert1", user);
//手动事务提交
//session.commit();
System.out.println(rows);
session.close();
}
}
6.Mybatis增删改查案例?
参见上面第5部分的代码,注意点如下:
- 查询所有
- 模糊查询:知识点是${ }连接占位符。这里导入了2个文件,即log4j-1.2.17.jar 和 log4j.properties(与mybatis.xml同目录)
- 插入数据:知识点是执行完sql语句后手动提交事务或获取session对象时选择不关闭自动提交的工厂方法。
7.Mybatis代理实现DAO开发?
原理:mapper的接口+mapper的映射
要求:
- mapper接口和mapper的映射同包同名(注册映射时方便)
- mapper映射文件的namespace和接口的完全限定名保持一致
- 标签的id和方法的名称保持一致
- 标签的输入参数类型和方法的参数类型一致
- 标签的输出结果类型和方法的返回值类型一致
代码(配置文件版):
User类
package pojo;
public class User {
private int uid;
private String uname;
private String password;
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [uid=" + uid + ", " + (uname != null ? "uname=" + uname + ", " : "") + (password != null ? "password=" + password : "") + "]";
}
}
UserMapper接口
package mapper;
import java.util.List;
import pojo.User;
public interface UserMapper {
List<User> queryAll();
User queryById(int uid);
}
UserMapper.xml文件(即UserMapper接口的映射文件,存放在mapper包下面哈,和UserMapper同包同名)
<?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="mapper.UserMapper">
<select id="queryAll" resultType="User">
select * from user
</select>
<select id="queryById" resultType="User">
select * from user where uid=#{}
</select>
</mapper>
Test类(获取配置,运行代码)
package test;
import java.io.IOException;
import java.io.Reader;
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 mapper.UserMapper;
public class Test {
public static void main(String[] args) throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = ssf.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
System.out.println(mapper.queryAll());
session.close();
}
}
代码(注解配置版)(关系比较简单时使用)
//在mapper接口中通过注解配置,就不需要mapper配置文件了!
//其他代码和配置仍然保持不变
public interface UserMapper {
@Select("select * from user")
List<User> queryAll();
@Select("select * from user where uid=#{uid}")
User queryById(int uid);
}
8.Mybatis高级映射
需求描述:查询部门及其部门下的所有员工。
映射分类:一对多和一对一
一对多:
- resultType特点
- 结果集中字段名称和类属性名称完全一致,此时将映射成功。
- 结果集中的字段和类的属性名称部分一致,部分映射成功。
- 结果集中的字段和类的属性名称都不一致,此时不会创建对象。
- 一对多:使用resultMap+collection标签
一对一:使用resultMap+association标签
代码实现:
项目目录结构
pojo类:
package pojo;
import java.util.List;
public class Dept {
private int deptno;
private String dname;
private String loc;
private List<Emp> emps;
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public List<Emp> getEmps() {
return emps;
}
public void setEmps(List<Emp> emps) {
this.emps = emps;
}
@Override
public String toString() {
return "Dept [deptno=" + deptno + ", " + (dname != null ? "dname=" + dname + ", " : "")
+ (loc != null ? "loc=" + loc + ", " : "") + (emps != null ? "emps=" + emps : "") + "]";
}
}
package pojo;
public class Emp {
private int empno;
private String ename;
private String job;
private int mgr;
private String hiredate;
private double sal;
private double comm;
private int deptno;
private Dept dept;
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public int getMgr() {
return mgr;
}
public void setMgr(int mgr) {
this.mgr = mgr;
}
public String getHiredate() {
return hiredate;
}
public void setHiredate(String hiredate) {
this.hiredate = hiredate;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public double getComm() {
return comm;
}
public void setComm(double comm) {
this.comm = comm;
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp [empno=" + empno + ", " + (ename != null ? "ename=" + ename + ", " : "")
+ (job != null ? "job=" + job + ", " : "") + "mgr=" + mgr + ", "
+ (hiredate != null ? "hiredate=" + hiredate + ", " : "") + "sal=" + sal + ", comm=" + comm
+ ", deptno=" + deptno + ", " + (dept != null ? "dept=" + dept : "") + "]";
}
}
全局配置文件: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>
<!-- 加载Java属性文件 -->
<properties resource="jdbc.properties"></properties>
<typeAliases>
<package name="pojo"/>
</typeAliases>
<!-- 配置环境 -->
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!-- name属性的值都是固定的,必须这么写,否则框架无法识别 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 批量注册映射文件:mappr接口和映射文件必须同包同名 -->
<package name="mapper"/>
</mappers>
</configuration>
mappr:接口+映射文件
package mapper;
import java.util.List;
import pojo.Dept;
public interface DeptMapper {
List<Dept> findDeptAndEmps();
}
package mapper;
import java.util.List;
import pojo.Emp;
public interface EmpMapper {
List<Emp> findEmpAndDept();
}
<?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="mapper.DeptMapper">
<!-- id:唯一标识 type:封装类型 -->
<resultMap type="Dept" id="deptMap">
<!--主键字段的映射 property:属性名称 column:结果集中字段名称
property的值必须与实体类中的属性名称一致,否则找不到setter访问器 -->
<id property="deptno" column="deptno" />
<result property="dname" column="dname" />
<result property="loc" column="loc" />
<!-- 将表中的数据封装到集合中 property:Dept类的属性
column:dept表和emp表的关联字段 ofType:dept类的属性emps集合中数据的类型 -->
<collection property="emps" ofType="Emp" column="deptno">
<id property="empno" column="empno" />
<result property="ename" column="ename" />
<result property="job" column="job" />
<result property="mgr" column="mgr" />
<result property="hiredate" column="hiredate" />
<result property="sal" column="sal" />
<result property="comm" column="comm" />
<result property="deptno" column="deptno" />
</collection>
</resultMap>
<!-- resultMap的值是上面resultMap标签的唯一标识id -->
<select id="findDeptAndEmps" resultMap="deptMap">
select * from dept left join emp on dept.deptno=emp.deptno
</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="mapper.EmpMapper">
<resultMap type="emp" id="empMap">
<id property="empno" column="empno"/>
<result property="ename" column="ename" />
<result property="job" column="job" />
<result property="mgr" column="mgr" />
<result property="hiredate" column="hiredate" />
<result property="sal" column="sal" />
<result property="comm" column="comm" />
<result property="deptno" column="deptno" />
<!-- 将结果集封装到对象中 property:Emp类的属性
column:emp表和dept表的关联字段 javaType:Emp类的属性的数据类型-->
<association property="dept" column="deptno" javaType="dept">
<id property="deptno" column="deptno" />
<result property="dname" column="dname"/>
<result property="loc" column="loc"/>
</association>
</resultMap>
<select id="findEmpAndDept" resultMap="empMap">
select * from emp left join dept on dept.deptno=emp.deptno
</select>
</mapper>
测试类:Test
package test;
import java.io.IOException;
import java.io.Reader;
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 mapper.DeptMapper;
import mapper.EmpMapper;
public class Test {
public static void main(String[] args) throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = ssf.openSession();
DeptMapper mapper = session.getMapper(DeptMapper.class);
System.out.println(mapper.findDeptAndEmps());
EmpMapper mapper1 = session.getMapper(EmpMapper.class);
System.out.println(mapper1.findEmpAndDept());
session.close();
}
}