Mybatis框架(Dao层实现+文件深入+多表操作)
- 1 Mybatis代理开发方式实现Dao层
- 2 Mybatis文件深入
- 2.1 映射文件深入(动态SQL)
- 2.1.1 动态sql-if
- 2.1.2 动态sql-foreach
- 2.1.3 动态sql的抽取
- 2.2 核心配置文件深入
- 2.2.1 typeHandlers标签
- 2.2.2 plugins标签(PageHelper分页)
- 3 Mybatis的多表操作
- 3.1 一对一查询
- 3.2 一对多查询
- 3.3 多对多查询
1 Mybatis代理开发方式实现Dao层
采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper 接口开发需要遵循以下规范:
1、 Mapper.xml文件中的namespace与mapper接口的全限定名相同
2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
1)dao层接口:
public interface UserMapper {
public List<User> findAll() throws IOException;
public User findById(int id);
}
2)映射文件配置:
<mapper namespace="lifeilin.dao.UserMapper">
<!-- 查询操作-->
<select id="findAll" resultType="user">
select *
from user
</select>
<!-- 根据id查询用户信息-->
<select id="findById" parameterType="int" resultType="user">
select *
from user
where id = #{id}
</select>
</mapper>
3)测试代码:
public class ServiceTest {
public static void main(String[] args) 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);
//测试有参数方法
User user = mapper.findById(3);
System.out.println(user);
}
}
2 Mybatis文件深入
2.1 映射文件深入(动态SQL)
2.1.1 动态sql-if
根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件。
接口:
public interface UserMapper {
public List<User> findByCondition(User user);
}
<mapper namespace="lifeilin.mapper.UserMapper">
<!-- 查询操作-->
<select id="findByCondition" parameterType="user" resultType="user">
select *
from user
<where>
-- 根据传进来的参数,如果满足if则动态拼接字符串查询
<if test="id!=0">
id = #{id}
</if>
<if test="username!=null">
and username = #{username}
</if>
<if test="password!=null">
and password = #{password}
</if>
</where>
</select>
</mapper>
public class MapperTest {
public void test() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//模拟参数条件user
User user = new User();
user.setId(3);
user.setUsername("张无忌");
// user.setPassword("123456");
List<User> userList = mapper.findByCondition(user);
System.out.println(userList);
}
}
2.1.2 动态sql-foreach
业务场景:查询id为1,2,3…的用户的信息
接口:
public List<User> findByIds(List<Integer> ids);
映射文件:
<mapper namespace="lifeilin.mapper.UserMapper">
<select id="findByIds" parameterType="list" resultType="user">
select *
from user
<where>
<foreach collection="list" open="id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
</mapper>
测试:
public class MapperTest {
public void test() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
List<User> userList = mapper.findByIds(list);
System.out.println(userList);
}
}
2.1.3 动态sql的抽取
2.2 核心配置文件深入
2.2.1 typeHandlers标签
自定义typeHandlers标签
可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现
org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个JDBC类型。
需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。
1)创建环境:
创建表:
接口:
public interface UserMapper {
public void save(User user);
}
映射文件:
<mapper namespace="lifeilin.mapper.UserMapper">
<insert id="save" parameterType="user">
insert into user
values (#{id}, #{username}, #{password}, #{birthday})
</insert>
</mapper>
测试:
public class UserMapperTest {
public void test() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(4);
user.setUsername("lifeilin");
user.setPassword("0000");
user.setBirthday(new Date()); //将Date类型转成表中的int类型会报错
mapper.save(user);
sqlSession.commit();
sqlSession.close();
}
}
报错:
2)自定义类型转换标签
① 定义转换类继承类BaseTypeHandler
② 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时 mysql的字符串类型转换成 java的Type类型的方法
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
public class DateTypeHandler extends BaseTypeHandler<Date> {
// 将Java类型转换成数据库中的类型
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
long time = date.getTime();
preparedStatement.setLong(i, time);
}
//将数据库中类型转成Java需要的类型
// String : 要转换字段的名字
// ResultSet : 查询结果集
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
//将long型转为Date
long aLong = resultSet.getLong(s);
Date date = new Date(aLong);
return date;
}
//将数据库中类型转成Java需要的类型
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
//将long型转为Date
long aLong = resultSet.getLong(i);
Date date = new Date(aLong);
return date;
}
//将数据库中类型转成Java需要的类型
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
//将long型转为Date
long aLong = callableStatement.getLong(i);
Date date = new Date(aLong);
return date;
}
}
③ 在MyBatis核心配置文件中进行注册
<!-- 注册类型处理器-->
<typeHandlers>
<typeHandler handler="lifeilin.handler.DateTypeHandler"></typeHandler>
</typeHandlers>
④ 测试转换是否正确
测试存数据:
测试取数据:
2.2.2 plugins标签(PageHelper分页)
① 导入通用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>
② 在mybatis核心配置文件中配置PageHelper插件
<!-- 配置分页助手插件:一般在mappers之前-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 指定方言-->
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
③ 测试分页数据获取
//查询全部
public void test3() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//设置分页相关参数 当前页+每页显示条数
PageHelper.startPage(1,3);
List<User> userList = mapper.findAll();
for (User user : userList
) {
System.out.println(user);
}
//其他分页的数据
PageInfo<User> pageInfo = new PageInfo<User>(userList);
System.out.println("总条数:"+pageInfo.getTotal());
System.out.println("总页数:"+pageInfo.getPages());
System.out.println("当前页:"+pageInfo.getPageNum());
System.out.println("每页显示长度:"+pageInfo.getPageSize());
System.out.println("是否第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否最后一页:"+pageInfo.isIsLastPage());
sqlSession.close();
}
3 Mybatis的多表操作
3.1 一对一查询
业务场景:order和user,根据查询order中的属性(包含user的id),将user中的属性也一起查询出来。
1)User实体:
2)Order实体:
3)在映射文件中手动配置字段与实体的映射关系:
<mapper namespace="lifeilin.mapper.OrderMapper">
<resultMap id="orderMap" type="lifeilin.domain.Order">
<!--手动指定字段与实体属性的映射关系
column:字段名称
property:实体属性名称
-->
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
<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 *,
`order`.id oid
FROM `order`,
`user`
WHERE `order`.uid = `user`.id;
</select>
</mapper>
4)测试:
public class OrderMapperTest {
public void test() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<Order> orderList = mapper.findAll();
for (Order order:orderList
) {
System.out.println(order);
}
sqlSession.close();
}
}
5)第二种在映射文件中手动配置字段与实体的映射关系的方法:
<mapper namespace="lifeilin.mapper.OrderMapper">
<resultMap id="orderMap" type="lifeilin.domain.Order">
<!--手动指定字段与实体属性的映射关系
column:字段名称
property:实体属性名称
-->
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
<!-- property:当前实体(order)中的属性名称(private User user)-->
<!-- javaType:当前实体(order)中的属性类型(User),这里使用了别名(user)-->
<association property="user" javaType="lifeilin.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 *,
`order`.id oid
FROM `order`,
`user`
WHERE `order`.uid = `user`.id;
</select>
</mapper>
3.2 一对多查询
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单
<mapper namespace="lifeilin.mapper.UserMapper">
<resultMap id="userMap" type="lifeilin.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="lifeilin.domain.order">
<!-- 封装order数据-->
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
</collection>
</resultMap>
<select id="findUserAll" resultMap="userMap">
SELECT *,
o.id oid
FROM `user` u,
`order` o
WHERE u.id = o.uid;
</select>
</mapper>
3.3 多对多查询
<mapper namespace="lifeilin.mapper.UserMapper">
<resultMap id="userAndRole" type="lifeilin.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="lifeilin.domain.Role">
<id column="roleId" property="id"></id>
<id column="roleName" property="roleName"></id>
<id column="roleDesc" property="roleDesc"></id>
</collection>
</resultMap>
<select id="findUserAndRole" resultMap="userAndRole">
SELECT *
FROM `user` u,
sys_user_role ur,
sys_role r
WHERE u.id = ur.userId
AND ur.roleId = r.id
</select>
</mapper>