文章目录
一 Mybatis简介
概述
- 基于Java的持久层框架,内部封装了jdbc,使开发者只需关注sql语句
- 通过xml或注解配置sql语句,通过Java对象和sql的动态参数进行映射生成最终执行的sql语句
- 执行sql将结果映射为Java对象并返回,采用ORM思想解决Java对象和数据表关系的映射问题
二 Mybatis XML配置开发
开发步骤
-
添加Mybatis坐标
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.45</version> </dependency>
-
创建user数据表
-
编写User实体类
-
编写映射文件UserMapper.xml
<!--namespace命名空间,与下面id组成标识--> <mapper namespace="userMapper"> <!--resultType查询结果的实体类型--> <select id="findAll" resultType="domain.User"> /*sql语句*/ select * from user </select> </mapper>
-
编写核心文件SqlMapConfig.xml
<configuration> <!--配置数据源环境--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value=""/> <property name="url" value=""/> <property name="username" value=""/> <property name="password" value=""/> </dataSource> </environment> </environments> <!--加载映射文件--> <mappers> <mapper resource="mapper/UserMapper.xml"></mapper> </mappers> </configuration>
-
编写测试类
@Test public void test1() throws IOException { //获得核心配置文件 InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml"); //获得session工厂对象 SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(resourceAsStream); //获得session会话对象 SqlSession sqlSession=sqlSessionFactory.openSession(); //执行操作 List<User> userList = sqlSession.selectList("userMapper.findAll"); System.out.println(userList); //释放资源 sqlSession.close(); }
数据库操作
增加操作
<!--parameterType传入参数类型-->
<insert id="save" parameterType="domain.User">
/*#{实体属性名}*/
insert into user values(#{id},#{username},#{password})
</insert>
@Test
public void test2() throws IOException {
User user=new User();
user.setId(2);
user.setUsername("aaa");
user.setPassword("123");
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession=sqlSessionFactory.openSession();
//执行操作
sqlSession.insert("userMapper.save",user);
//执行更新操作,需要提交事务
sqlSession.commit();
sqlSession.close();
}
修改操作
<update id="update" parameterType="domain.User">
update user set username=#{username},password=#{password} where id=#{id}
</update>
@Test
public void test3() throws IOException {
User user=new User();
user.setId(2);
user.setUsername("bbb");
user.setPassword("1234");
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession=sqlSessionFactory.openSession();
//执行操作
sqlSession.update("userMapper.update",user);
//执行更新操作,需要提交事务
sqlSession.commit();
sqlSession.close();
}
删除操作
<delete id="delete" parameterType="int">
/*#{任意字符串}引用单个参数*/
delete from user where id=#{id}
</delete>
@Test
public void test4() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession=sqlSessionFactory.openSession();
//执行操作
sqlSession.delete("userMapper.delete",2);
//执行更新操作,需要提交事务
sqlSession.commit();
sqlSession.close();
}
查询操作
看前面“开发步骤”
核心配置文件
- environments标签
配置数据库环境,支持多环境配置
<!--配置数据源环境,default是默认环境-->
<environments default="development">
<!--环境名称-->
<environment id="development">
<!--事务管理器类型,取值有JDBC(常用)、MANAGED(由容器管理事务)-->
<transactionManager type="JDBC"></transactionManager>
<!--数据源类型,取值有POOLED(连接池,常用)、UNPOOLED、JNDI-->
<dataSource type="POOLED">
<!--数据源配置基本参数-->
<property name="driver" value=""/>
<property name="url" value=""/>
<property name="username" value=""/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
- mapper(s)标签
加载映射文件,加载方式有
1 使用相对于类路径的资源引用(常用)
<!--加载映射文件-->
<mappers>
<mapper resource="mapper/UserMapper.xml"></mapper>
</mappers>
2 使用URL
3 使用映射器接口实现类的完全限定类名
4 将包内的映射器接口实现全部注册为映射器
- properties标签
加载外部properties文件
<!--加载外部properties文件-->
<properties resource="classpath:jdbc.properties"></properties>
<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>
- typeAliase(s)标签
为类型定义别名,注意标签在文件中的位置
<!--定义别名-->
<typeAliases>
<typeAlias type="domain.User" alias="user"></typeAlias>
</typeAliases>
<select id="findAll" resultType="user">
select * from user
</select>
- typeHandlers标签
重写类型处理器或自定义类型处理器处理不支持或非标准的类型
- 定义转换类继承BaseTypeHandler,T为Java类型
- 覆盖4个未实现的方法
- 在核心配置文件注册
- 测试
以Java的Date类型与Mysql的bigint类型转换为例
public class DateTypeHandler extends BaseTypeHandler<Date> {
//java类型转成数据库类型
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
long time = parameter.getTime();
ps.setLong(i,time);
}
//下面三个都是数据库类型转成java类型
@Override
public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
//获取结果集需要的数据long转成Date
long aLong = rs.getLong(columnName);
return new Date(aLong);
}
@Override
public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
long aLong = rs.getLong(columnIndex);
return new Date(aLong);
}
@Override
public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
long aLong = cs.getLong(columnIndex);
return new Date(aLong);
}
}
<!--注册类型处理器-->
<typeHandlers>
<typeHandler handler="handler.DateTypeHandler"></typeHandler>
</typeHandlers>
- plugins标签
配置第三方插件,以分页插件PageHelper为例
- 导入插件坐标
- 在核心配置文件配置插件
- 测试
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
<!--配置分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
UserMapper mapper=sqlSession.getMapper(UserMapper.class);
//设置分页参数 当前页+每页多少条
PageHelper.startPage(1,2);
List<User> userList=mapper.findAll();
//获得与分页相关参数
PageInfo<User> pageInfo=new PageInfo<User>(userList);
pageInfo.getPageNum();//当前页
pageInfo.getPageSize();//每页条数
pageInfo.getTotal();//总条数
pageInfo.getPages();//总页数
相应API
- SqlSessionFactoryBuilder
SqlSessionFactory build(InputStream inputStream);//加载mybatis核心文件的输入流构建SqlSessionFactory对象
- SqlSessionFactory
创建SqlSession对象
SqlSession openSession();//事务不会自动提交
SqlSession openSession(boolean autoCommit);//autoCommit设置为true,事务会自动提交
- SqlSession
selectOne();//查询一个
selectList();//查询多个
insert();//插入
update();//更新
delete();//删除
commit();//提交事务
rollback();//回滚事务
close();//释放连接
Dao层实现
传统方式
public interface UserMapper {
public List<User> findAll() throws IOException;
}
public class UserMapperImpl implements UserMapper {
@Override
public List<User> findAll() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession=sqlSessionFactory.openSession();
List<User> userList = sqlSession.selectList("userMapper.findAll");
sqlSession.close();
return userList;
}
}
接口代理方式
只需要编写Dao接口,由Mybatis根据接口定义创建接口的动态代理对象,Dao接口遵循以下规范
- Dao接口的全限定名与Mapper.xml中mapper标签的namespace相同
- Dao接口的方法名与Mapper.xml中定义的每个statement的id相同
- Dao接口方法的参数类型与Mapper.xml中定义的每个statement的parameterType相同
- Dao接口方法的返回类型与Mapper.xml中定义的每个statement的resultType相同
public interface UserMapper {
public List<User> findAll() throws IOException;
}
<mapper namespace="dao.UserMapper">
<select id="findAll" resultType="User">
select * from user
</select>
</mapper>
@Test
public void test5() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession=sqlSessionFactory.openSession();
//获得动态代理对象
UserMapper mapper=sqlSession.getMapper(UserMapper.class);
List<User> userList=mapper.findAll();
System.out.println(userList);
sqlSession.close();
}
映射文件深入-动态sql
<if>
条件判断
<select id="findByCondition" parameterType="domain.User" resultType="domain.User">
select * from user
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
<if test="password!=null">
and password=#{password}
</if>
</where>
</select>
<foreach>
循环
<select id="findByIds" parameterType="list" resultType="domain.User">
/*select * from user where id in(1,2,3)*/
select * from user
<where>
/*collection取值list或者array,item为集合或数组的每一项名称,separator为集合或数组的每一项的分隔符*/
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
<sql>,<include>
sql语句抽取
<sql id="selectUser">select * from user</sql>
<select id="findAll" resultType="user">
<include refid="selectUser"></include>
</select>
多表操作
orders表有id, uid字段,uid参照user表id
role表有id, name字段
user_role表有uid, rid字段,uid参照user表id,rid参照role表id
- 一对一
以下两种写法都行
<mapper namespace="dao.OrderMapper">
<resultMap id="orderMap" type="domain.Order">
<!--手动指定字段与实体属性的映射关系
column数据表的字段
property实体属性-->
<id column="oid" property="id"></id>
<result column="uid" property="user.id"></result>
<result column="username" property="user.username"></result>
<result column="password" property="user.password"></result>
<result column="birthday" property="user.birthday"></result>
</resultMap>
<select id="findAll" resultMap="orderMap">
select *,o.id oid from orders o,user u where o.uid=u.id
</select>
</mapper>
<mapper namespace="dao.OrderMapper">
<resultMap id="orderMap" type="domain.Order">
<!--手动指定字段与实体属性的映射关系
column数据表的字段
property实体属性-->
<id column="oid" property="id"></id>
<!--property当前实体的属性名称
javaType当前实体的属性类型-->
<association property="user" javaType="domain.User">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
<select id="findAll" resultMap="orderMap">
select *,o.id oid from orders o,user u where o.uid=u.id
</select>
</mapper>
- 一对多
<mapper namespace="dao.UserMapper">
<resultMap id="userMap" type="domain.User">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<!--配置集合
property集合名称
ofType集合中数据类型-->
<collection property="orderList" ofType="domain.Order">
<id column="oid" property="id"></id>
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
select *,o.id oid from user u,orders o where o.uid=u.id
</select>
</mapper>
- 多对多
<resultMap id="userRoleMap" type="domain.User">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<collection property="roleList" ofType="domain.Role">
<id column="rid" property="id"></id>
<result column="name" property="name"></result>
</collection>
</resultMap>
<select id="findUserAndRole" resultMap="userRoleMap">
select * from user u,user_role ur,role r where u.id=ur.uid and ur.rid=r.id
</select>
三 Mybatis注解开发
增删改查
在接口方法上使用注解编写sql语句
public interface UserMapper {
@Select("select * from user")
public List<User> findAll();
@Insert("insert into user values (#{id}, #{username}, #{password},#{birthday})")
public void save(User user);
@Update("update user set username=#{username},password=#{password} where id = #{id}")
public void update(User user);
@Delete("delete from user where id = #{id}")
public void delete(int id);
}
核心配置文件配置映射关系
<mappers>
<!--指定接口所在的包-->
<package name="dao"/>
</mappers>
多表操作
- 一对一
以下两种写法都可以
public interface OrderMapper {
@Select("select *,o.id oid from orders o,user u where o.uid=u.id")
@Results({
@Result(column="oid",property="id"),
@Result(column="uid",property="user.id"),
@Result(column="username",property="user.username"),
@Result(column="password",property="user.password"),
@Result(column="birthday",property="user.birthday")
})
public List<Order> findAll();
}
public interface OrderMapper {
@Select("select * from orders")
@Results({
@Result(column="id",property="id"),
@Result(
property="user",//要封装的属性名称
column="uid",//根据哪个字段查询user表
javaType = User.class,//要封装的实体
one=@One(select = "dao.UserMapper.findById")
)
})
public List<Order> findAll();
}
- 一对多
public interface UserMapper {
@Select("select * from user")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "username", property = "username"),
@Result(column = "password", property = "password"),
@Result(column = "birthday", property = "birthday"),
@Result(
property = "orderList",
column = "id",
javaType = List.class,
many = @Many(select = "dao.OrderMapper.findByUid")
)
})
public List<User> findUserAndOrder();
}
- 多对多
public interface UserMapper {
@Select("select * from user")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "username", property = "username"),
@Result(column = "password", property = "password"),
@Result(column = "birthday", property = "birthday"),
@Result(
property = "roleList",
column = "id",
javaType = List.class,
many = @Many(select = "dao.RoleMapper.findByUid")
)
})
public List<User> findUserAndRole();
}