MyBatis
MyBatis官方学习网站
http://www.mybatis.org/mybatis-3/zh/index.html
为什么需要MyBatis?
Jdbc操作数据库的不足之处
1、需要通过硬编码的方式建立到数据库的连接
2、需要通过硬编码的方式来创建PreparedStatment对象
3、频繁的关闭连接、建立连接,导致系统的性能会下降
MyBatis
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。Mybatis框架封装了jdbc的一个持久层的框架,它和Hibernate都属于ORM框架,Hibernate是一个完全的orm框架,Mybatis不是一个完全的orm框架,不需要关注数据库连接的建立、Statement或PreparedStatement的创建,更多的去关注sql语句本身。
通过下面的项目来了解MyBatis
项目目录结构
本项目使用的Oracle数据库,项目对象的表为user_tab
src目录下是一个是MyBatis的配置文件
<?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>
<!-- 指定了默认的环境为development -->
<environments default="development">
<!-- 指出了环境的唯一标识 -->
<environment id="development">
<!-- 指出了事务管理器 -->
<transactionManager type="JDBC"/>
<!-- 指出了连接池,并指出了连接数据库的驱动,url,用户名,密码 -->
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>
<property name="username" value="scott"/>
<property name="password" value="itcast"/>
</dataSource>
</environment>
</environments>
<!-- 注册映射文件,路径中.都用/代替 -->
<mappers>
<mapper resource="com/ghq/model/dao/impl/UserDaoImpl.xml"/>
</mappers>
</configuration>
表所对应的实体类为User.java
package com.ghq.model.entity;
import java.util.Date;
public class User {
private int id;
private String username;
private String userpass;
private Date birthday;
private int height;
private String gender;
public User(int id, String username, String userpass, Date birthday,
int height, String gender) {
this.id = id;
this.username = username;
this.userpass = userpass;
this.birthday = birthday;
this.height = height;
this.gender = gender;
}
public User(String username, String userpass, Date birthday, int height,
String gender) {
this.username = username;
this.userpass = userpass;
this.birthday = birthday;
this.height = height;
this.gender = gender;
}
public User() {
}
//省略setter/getter方法
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", userpass="
+ userpass + ", birthday=" + birthday + ", height=" + height
+ ", gender=" + gender + "]";
}
}
工具类MyBatisDb.java
主要的功能是获取获取SqlSessionFactory以及SqlSession的对象,SqlSession中具有对数据表增删改查的方法。
package com.ghq.model.utils;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MybatisDb {
private static String config = "mybatis-config.xml";
private static SqlSessionFactory sqlSessionFac;
static{
try {
//读取配置文件
InputStream inputstream = Resources.getResourceAsStream(config);
//获取SqlSessionFactory对象
sqlSessionFac = new SqlSessionFactoryBuilder().build(inputstream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession对象,并且开启了事务
public static SqlSession getSession(){
return sqlSessionFac.openSession();
}
}
UserDao.java 是一个接口,其中的内容主要是实现用户的增删改查功能
package com.ghq.model.dao;
import java.util.List;
import com.ghq.model.entity.User;
public interface UserDao {
//根据id查询用户
User getUserById(int id);
//根据模糊姓名和性别查询
List<User> getUserBynameAndGender(User u);
//新增用户
boolean addUser(User u);
//修改用户
boolean updateUser(User u);
//删除用户
boolean delUser(int id);
}
UserDaoImpl.java是UserDao的实现类
package com.ghq.model.dao.impl;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.ghq.model.dao.UserDao;
import com.ghq.model.entity.User;
import com.ghq.model.utils.MybatisDb;
public class UserDaoImpl implements UserDao {
//通过id获取用户的方法
@Override
public User getUserById(int id) {
//获得SQLSession对象
SqlSession session = MybatisDb.getSession();
//执行com.ghq.model.dao.impl.UserDaoImpl.getUserById标识的SQL语句,在执行前为占位参数赋值
User u = session.selectOne("com.ghq.model.dao.impl.UserDaoImpl.getUserById", id);
session.close();
return u;
}
//根据模糊姓名和性别查询
@Override
public List<User> getUserBynameAndGender(User u) {
//获得SQLSession对象
SqlSession session = MybatisDb.getSession();
List<User> ulist = session.selectList("com.ghq.model.dao.impl.UserDaoImpl.getUserBynameAndGender", u);
session.close();
return ulist;
}
//新增用户
@Override
public boolean addUser(User u) {
SqlSession session = MybatisDb.getSession();
int row = session.insert("com.ghq.model.dao.impl.UserDaoImpl.addUser", u);
session.commit();
session.close();
if (row > 0) {
return true;
}
return false;
}
//修改用户
@Override
public boolean updateUser(User u) {
SqlSession session = MybatisDb.getSession();
int row = session.update("com.ghq.model.dao.impl.UserDaoImpl.updateUser", u);
session.commit();
session.close();
if (row > 0) {
return true;
}
return false;
}
//删除用户
@Override
public boolean delUser(int id) {
SqlSession session = MybatisDb.getSession();
int row = session.delete("com.ghq.model.dao.impl.UserDaoImpl.delUser", id);
session.commit();
session.close();
if (row > 0) {
return true;
}
return false;
}
}
要想实现对数据库的操作,必须配置sql的映射文件UserdaoImpl.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语句标识 -->
<mapper namespace="com.ghq.model.dao.impl.UserDaoImpl">
<!-- 映射文件中的SQL语句没有分号
#{}相当于占位符? #{}中名字标识输入参数的名字,如果输入参数是简单类型,则#{}中参数名任意
parameterType指出了输入参数类型之外的类型
resultType是结果类型,即返回的记录中单条记录的类型
-->
<select id="getUserById" parameterType="int" resultType="com.ghq.model.entity.User">
select * from user_tab where id = #{id}
</select>
<!-- 输入参数类型不是简单类型,#{}中的输入参数的名字不能任意,参数类型是自定义的实体类型,#{}中使用的是属性名,模糊查询不使用#{},而使用${} #{}两端没有单引号 -->
<select id="getUserBynameAndGender" parameterType="com.ghq.model.entity.User" resultType="com.ghq.model.entity.User">
select * from user_tab where username like '%${username}%' and gender = #{gender}
</select>
<insert id="addUser" parameterType="com.ghq.model.entity.User" >
<! -- 新增用户中,用户id是通过序列来实现自动增长,所以需在数据库中创建user_tb_seq序列,然后通过select语句查询出下一个序列值,封装到标志属性中。order指出了查询是否在新增之前,是的话为BEFORE,否则为AFTER,resultType指出了查询的数据的类型,不能省略 -->
<selectKey keyProperty="id" order="BEFORE" resultType="int">
select user_tb_seq.nextval from dual
</selectKey>
insert into user_tab (id,username,userpass,birthday,height,gender) values
(#{id},#{username},#{userpass},#{birthday},#{height},#{gender})
</insert>
<update id="updateUser" parameterType="com.ghq.model.entity.User">
update user_tab set username = #{username},userpass = #{userpass},birthday = #{birthday},
height = #{height},gender = #{gender} where id = #{id}
</update>
<delete id="delUser" parameterType="int">
delete from user_tab where id = #{id}
</delete>
</mapper>
使用JUnit4进行单元测试testmybatis.java
package com.ghq.test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import org.junit.Test;
import com.ghq.model.dao.UserDao;
import com.ghq.model.dao.impl.UserDaoImpl;
import com.ghq.model.entity.User;
public class testmybatis {
@Test
public void testgetUserbyID(){
UserDao userdao = new UserDaoImpl();
User u = userdao.getUserById(7);
System.out.println(u);
}
@Test
public void testgetUserbynameAndGender(){
UserDao userdao = new UserDaoImpl();
User user = new User();
user.setUsername("张");
user.setGender("女");
List<User> ulist = userdao.getUserBynameAndGender(user);
for (User uu : ulist) {
System.out.println(uu);
}
}
@Test
public void testaddUser(){
UserDao userdao = new UserDaoImpl();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String str = "1990-12-12";
Date d = null;
try {
d = dateFormat.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
User user = new User("张三","zs",d,160,"女");
boolean flag = userdao.addUser(user);
if (flag) {
System.out.println("插入成功");
}else{
System.out.println("插入失败");
}
}
@Test
public void testupdateUser(){
UserDao userdao = new UserDaoImpl();
User user = new User(1,"李四 ","ls",new Date(),170,"男");
boolean flag = userdao.updateUser(user);
if (flag) {
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
}
@Test
public void testdelUser(){
UserDao userdao = new UserDaoImpl();
boolean flag = userdao.delUser(1);
if (flag) {
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
}
}
Mapper接口
MyBatis的项目中,可以没有dao的实现类。
Mybatis 提供了 Mapper接口的代理对象,在执行 Mapper接口方法时,实际执行的是Mybatis的代理对象,代理对象在 invoke 方法内获取 Mapper接口类全名+方法全名 作为statement的ID,然后通过ID去Statement匹配注册的SQL,然后使用 SqlSession 执行这个 SQL。
实现这样的功能前提是映射文件和接口主文件名相同,且在同一个包下,映射文件的命名空间是接口的全限定名。
在mybatis-config.xml配置文件中
1、注册映射文件时,若映射文件过多,推荐使用批量注册映射文件。
批量注册映射文件的前提是:接口的主文件名要和相应映射文件名一致,而且映射文件中的namespace是接口的全限定名,接口和映射文件在同一个包下
<!-- 注册映射文件(批量注册映射文件,注册com.ghq.model.dao下的所有配置文件) -->
<mappers>
<package name="com.ghq.model.dao"/>
</mappers>
2、在配置文件中定义别名,可以在映射文件中使用别名。
<!-- 在配置文件中定义别名,可以在映射文件中使用别名 -->
<typeAliases>
<!-- 一次定义一个别名 -->
<!-- <typeAlias type="com.ghq.model.entity.User" alias="user"/> -->
<!-- 为该包下的实体类定义别名 -->
<package name="com.ghq.model.entity"/>
</typeAliases>
定义别名后,可以在resultType或paramterType中使用user
3、可以将连接数据库的驱动,url,用户名和密码存放在properties文件中。
db.properties文件中的配置
db.driver=oracle.jdbc.driver.OracleDriver
db.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
db.username=scott
db.password=itcast
<!-- 加载db.properties文件 -->
<properties resource="db.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
以根据id查询用户为例:
UserDao中的代码
public interface UserDao {
//根据id查询用户
User getUserById(int id);
}
UserDao的映射文件UserDao.xml
<mapper namespace="com.ghq.model.dao.UserDao">
<select id="getUserById" parameterType="int" resultType="user">
select * from user_tab where id = #{id}
</select>
</mapper>
单元测试的代码
public class testmybatis {
@Test
public void testgetUserbyID(){
SqlSession session = MybatisDb.getSession();
UserDao userdao = session.getMapper(UserDao.class);
User u = userdao.getUserById(1);
System.out.println(u);
}
}
动态sql语句
动态sql语句涉及的元素
1、where
where元素默认去除第一个and,如果参数输入不合法,则会删掉where子句
2、if
对输入参数进行条件判断,if标签中test属性中的不存在&&,使用and代替
3、foreach
用于产生一个集合
工具类Uservo.java
package com.ghq.model.entity;
import java.util.List;
public class UserVo {
private List<Integer> idlist;
public List<Integer> getIdlist() {
return idlist;
}
public void setIdlist(List<Integer> idlist) {
this.idlist = idlist;
}
}
UserDao.java
//根据模糊姓名和性别查询
List<User> getUserBynameAndGender(Map<String,Object> m);
//查询一个主键范围的用户列表
public List<User> getUsersWithIdList(UserVo vo);
UserDao.xml
<select id="getUserBynameAndGender" parameterType="java.util.Map" resultType="user">
select * from user_tab
<!-- where 标签默认会去除第一个and,若输入参数是null,则删除where条件 -->
<where>
<if test="username != null and username != ''">
and username like '%${username}%'
</if>
<if test="gender !=null and gender !=''">
and gender = #{gender}
</if>
</where>
</select>
<select id="getUsersWithIdList" parameterType="UserVo" resultType="user">
select * from user_tab
<where>
<if test="idlist !=null">
and id in
<foreach collection="idlist" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
单元测试
public class testmybatis {
@Test
public void testgetUserbynameAndGender(){
SqlSession session = MybatisDb.getSession();
UserDao userdao = session.getMapper(UserDao.class);
Map<String,Object> user = new HashMap<String,Object>();
user.put("username", "张");
user.put("gender", "女");
List<User> ulist = userdao.getUserBynameAndGender(user);
for (User uu : ulist) {
System.out.println(uu);
}
}
//批量查询id为1,2,3的用户
@Test
public void testgetUsersWithIdList(){
SqlSession session = MybatisDb.getSession();
UserDao userdao = session.getMapper(UserDao.class);
UserVo vo = new UserVo();
List<Integer> idlist = new ArrayList<Integer>();
idlist.add(1);
idlist.add(2);
idlist.add(3);
vo.setIdlist(idlist);
List<User> list = userdao.getUsersWithIdList(vo);
for (User user : list) {
System.out.println(user);
}
}
}
select元素中resultMap属性
用于解决查询数据表时,查询出的列名和实体的属性名不一致,该属性值要和
resultMap元素的id一致,该resultMap元素就来完成查询出的列名和实体的属性名的映射,如果查询出的列名和实体的属性名一致,select元素使用resultType就可以,不需要使用resultMap属性。