admin管理员组文章数量:1794759
MyBatis详解
MyBatis 一、Mybatis概述 1.1 什么是Mybatis
mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
1.2 Mybatis的特点- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。
- sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的orm字段关系映射 提供对象关系映射标签,支持对象关系组建维护 提供xml标签,支持编写动态sql。
①添加MyBatis的坐标
②创建user数据表
③编写User实体类
④编写映射文件UserMapper.xml
⑤编写核心文件SqlMapConfig.xml
⑥编写测试类
2.2 环境搭建1)导入MyBatis的坐标和其他相关坐标
<!--mybatis坐标--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <!--mysql驱动坐标--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> <scope>runtime</scope> </dependency> <!--单元测试坐标--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!--日志坐标--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency>2)创建user数据表 3) 编写User实体
public class User { private int id; private String username; private String password; //省略get个set方法 }4)编写UserMapper映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis//DTD Mapper 3.0//EN" "mybatis/dtd/mybatis-3-mapper.dtd"> <mapper namespace="userMapper"> <select id="findAll" resultType="com.itheima.domain.User"> select * from User </select> </mapper>5) 编写MyBatis核心文件
<!DOCTYPE configuration PUBLIC "-//mybatis//DTD Config 3.0//EN“ "mybatis/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///test"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/itheima/mapper/UserMapper.xml"/> </mappers> </configuration> 2.3 编写测试代码 //加载核心配置文件 InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml"); //获得sqlSession工厂对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); //获得sqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //执行sql语句 List<User> userList = sqlSession.selectList("userMapper.findAll"); //打印结果 System.out.println(userList); //释放资源 sqlSession.close(); 三、MyBatis的映射文件概述 3.1 映射文件的DTD约束头 <!DOCTYPE mapper PUBLIC "-//mybatis//DTD Mapper 3.0//EN" "mybatis/dtd/mybatis-3-mapper.dtd"> 3.2 根标签 <mapper namespace="userMapper"> 3.3 字标签 <select id="findAll" resultType="com.study.domain.User"> 3.4 动态SQL语句 3.4.1 动态 SQL语句概述Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了.
3.4.2、动态SQL之 <if>我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。 配置文件代码
<select id="findByCondition" parameterType="user" resultType="user"> select * from User <where> <if test="id!=0"> and id=#{id} </if> <if test="username!=null"> and username=#{username} </if> </where> </select>当查询条件id和username都存在时,控制台打印的sql语句如下:
… … … //获得MyBatis框架生成的UserMapper接口的实现类 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User condition = new User(); condition.setId(1); condition.setUsername("lucy"); User user = userMapper.findByCondition(condition); … … …当查询条件只有id存在时,控制台打印的sql语句如下:
… … … //获得MyBatis框架生成的UserMapper接口的实现类 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User condition = new User(); condition.setId(1); User user = userMapper.findByCondition(condition); … … … 3.4.2、动态SQL之 <foreach>循环执行sql的拼接操作,例如:SELECT * FROM USER WHERE id IN (1,2,5)。
<select id="findByIds" parameterType="list" resultType="user"> select * from User <where> <foreach collection="array" open="id in(" close=")" item="id" separator=","> #{id} </foreach> </where> </select>测试代码片段如下:
… … … //获得MyBatis框架生成的UserMapper接口的实现类 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); int[] ids = new int[]{2,5}; List<User> userList = userMapper.findByIds(ids); System.out.println(userList); … … …<foreach>标签用于遍历集合,它的属性:
•collection:代表要遍历的集合元素,注意编写时不要写#{}
•open:代表语句的开始部分
•close:代表结束部分
•item:代表遍历集合的每个元素,生成的变量名
•sperator:代表分隔符
3.5 SQL片段抽取Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
<!--抽取sql片段简化编写--> <sql id="selectUser" select * from User</sql> <select id="findById" parameterType="int" resultType="user"> <include refid="selectUser"></include> where id=#{id} </select> <select id="findByIds" parameterType="list" resultType="user"> <include refid="selectUser"></include> <where> <foreach collection="array" open="id in(" close=")" item="id" separator=","> #{id} </foreach> </where> </select> 四、MyBatis核心配置文件深入 4.1 typeHandlers标签无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器(截取部分)。 开发步骤:
①定义转换类继承类BaseTypeHandler
②覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时 mysql的字符串类型转换成 java的Type类型的方法
③在MyBatis核心配置文件中进行注册
测试转换是否正确
public class MyDateTypeHandler extends BaseTypeHandler<Date> { public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType type) { preparedStatement.setString(i,date.getTime()+""); } public Date getNullableResult(ResultSet resultSet, String s) throws SQLException { return new Date(resultSet.getLong(s)); } public Date getNullableResult(ResultSet resultSet, int i) throws SQLException { return new Date(resultSet.getLong(i)); } public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException { return callableStatement.getDate(i); } } <!--注册类型自定义转换器--> <typeHandlers> <typeHandler handler="com.itheima.typeHandlers.MyDateTypeHandler"></typeHandler> </typeHandlers>测试添加操作:
user.setBirthday(new Date()); userMapper.add2(user);数据库数据 测试查询操作
4.2 plugins标签MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
开发步骤:
①导入通用PageHelper的坐标
②在mybatis核心配置文件中配置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插件
<!-- 注意:分页助手的插件 配置在通用馆mapper之前 --> <plugin interceptor="com.github.pagehelper.PageHelper"> <!-- 指定方言 --> <property name="dialect" value="mysql"/> </plugin>③测试分页代码实现
@Test public void testPageHelper(){ //设置分页参数 PageHelper.startPage(1,2); List<User> select = userMapper2.select(null); for(User user : select){ System.out.println(user); } }获得分页相关的其他参数
//其他分页的数据 PageInfo<User> pageInfo = new PageInfo<User>(select); 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()); 五、 Mybatis多表查询 5.1 一对一查询 5.1.1 一对一查询的模型用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
5.1.2一对一查询的语句对应的sql语句:select * from orders o,user u where o.uid=u.id;
查询的结果如下:
5.1.3 创建Order和User实体 public class Order { private int id; private Date ordertime; private double total; //代表当前订单从属于哪一个客户 private User user; } public class User { private int id; private String username; private String password; private Date birthday; } 5.1.4 创建OrderMapper接口 public interface OrderMapper { List<Order> findAll(); } 5.1.5 配置OrderMapper.xml <mapper namespace="com.itheima.mapper.OrderMapper"> <resultMap id="orderMap" type="com.itheima.domain.Order"> <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 * from orders o,user u where o.uid=u.id </select> </mapper>其中<resultMap>还可以配置如下:
<resultMap id="orderMap" type="com.itheima.domain.Order"> <result property="id" column="id"></result> <result property="ordertime" column="ordertime"></result> <result property="total" column="total"></result> <association property="user" javaType="com.itheima.domain.User"> <result column="uid" property="id"></result> <result column="username" property="username"></result> <result column="password" property="password"></result> <result column="birthday" property="birthday"></result> </association> </resultMap> 5.1.6 测试结果 OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); List<Order> all = mapper.findAll(); for(Order order : all){ System.out.println(order); } 5.2 一对多查询 5.2.1 一对多查询的模型用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单
5.2.2 一对多查询的语句对应的sql语句:select *,o.id oid from user u left join orders o on u.id=o.uid;
查询的结果如下:
5.2.3 修改User实体 public class Order { private int id; private Date ordertime; private double total; //代表当前订单从属于哪一个客户 private User user; } public class User { private int id; private String username; private String password; private Date birthday; //代表当前用户具备哪些订单 private List<Order> orderList; } 5.2.4 创建UserMapper接口 public interface UserMapper { List<User> findAll(); } 5.2.5 配置UserMapper.xml <mapper namespace="com.itheima.mapper.UserMapper"> <resultMap id="userMap" type="com.itheima.domain.User"> <result column="id" property="id"></result> <result column="username" property="username"></result> <result column="password" property="password"></result> <result column="birthday" property="birthday"></result> <collection property="orderList" ofType="com.itheima.domain.Order"> <result column="oid" property="id"></result> <result column="ordertime" property="ordertime"></result> <result column="total" property="total"></result> </collection> </resultMap> <select id="findAll" resultMap="userMap"> select *,o.id oid from user u left join orders o on u.id=o.uid </select> </mapper> 5.2.6 测试结果 UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> all = mapper.findAll(); for(User user : all){ System.out.println(user.getUsername()); List<Order> orderList = user.getOrderList(); for(Order order : orderList){ System.out.println(order); } System.out.println("----------------------------------"); } 5.3 多对多查询 5.3.1 多对多查询模型用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用多对多查询的需求:查询用户同时查询出该用户的所有角色。
5.3.2 多对多查询的语句对应的sql语句:select u.,r.,r.id rid from user u left join user_role ur on u.id=ur.user_id inner join role r on ur.role_id=r.id;
查询的结果如下:
5.3.3创建Role实体,修改User实体 public class User { private int id; private String username; private String password; private Date birthday; //代表当前用户具备哪些订单 private List<Order> orderList; //代表当前用户具备哪些角色 private List<Role> roleList; } public class Role { private int id; private String rolename; } 5.3.4 添加UserMapper接口方法 List<User> findAllUserAndRole(); 5.3.5 配置UserMapper.xml <resultMap id="userRoleMap" type="com.itheima.domain.User"> <result column="id" property="id"></result> <result column="username" property="username"></result> <result column="password" property="password"></result> <result column="birthday" property="birthday"></result> <collection property="roleList" ofType="com.itheima.domain.Role"> <result column="rid" property="id"></result> <result column="rolename" property="rolename"></result> </collection> </resultMap> <select id="findAllUserAndRole" resultMap="userRoleMap"> select u.*,r.*,r.id rid from user u left join user_role ur on u.id=ur.user_id inner join role r on ur.role_id=r.id </select> 5.3.6 测试结果 UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> all = mapper.findAllUserAndRole(); for(User user : all){ System.out.println(user.getUsername()); List<Role> roleList = user.getRoleList(); for(Role role : roleList){ System.out.println(role); } System.out.println("----------------------------------"); } 六、Mybatis的注解开发 6.1 MyBatis的常用注解这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper
映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
6.2 MyBatis的增删改查我们完成简单的user表的增删改查的操作
private UserMapper userMapper; @Before public void before() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(true); userMapper = sqlSession.getMapper(UserMapper.class); } @Test public void testAdd() { User user = new User(); user.setUsername("测试数据"); user.setPassword("123"); user.setBirthday(new Date()); userMapper.add(user); } @Test public void testUpdate() throws IOException { User user = new User(); user.setId(16); user.setUsername("测试数据修改"); user.setPassword("abc"); user.setBirthday(new Date()); userMapper.update(user); } @Test public void testDelete() throws IOException { userMapper.delete(16); } @Test public void testFindById() throws IOException { User user = userMapper.findById(1); System.out.println(user); } @Test public void testFindAll() throws IOException { List<User> all = userMapper.findAll(); for(User user : all){ System.out.println(user); } }修改MyBatis的核心配置文件,我们使用了注解替代的映射文件,所以我们只需要加载使用了注解的Mapper接口即可
<mappers> <!--扫描使用注解的类--> <mapper class="com.itheima.mapper.UserMapper"></mapper> </mappers>或者指定扫描包含映射关系的接口所在的包也可以
<mappers> <!--扫描使用注解的类所在的包--> <package name="com.itheima.mapper"></package> </mappers> 6.3 MyBatis的注解实现复杂映射开发实现复杂关系映射之前我们可以在映射文件中通过配置来实现,使用注解开发后,我们可以使用@Results注解,@Result注解,@One注解,@Many注解组合完成复杂关系的配置
6.4 一对一查询 6.4.1 一对一查询的模型用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
6.4.2 一对一查询的语句对应的sql语句:
select * from orders; select * from user where id=查询出订单的uid;查询的结果如下:
6.4.3 创建Order和User实体 public class Order { private int id; private Date ordertime; private double total; //代表当前订单从属于哪一个客户 private User user; } public class User { private int id; private String username; private String password; private Date birthday; } 6.4.4 创建OrderMapper接口 public interface OrderMapper { List<Order> findAll(); } 6.4.5 使用注解配置Mapper public interface OrderMapper { @Select("select * from orders") @Results({ @Result(id=true,property = "id",column = "id"), @Result(property = "ordertime",column = "ordertime"), @Result(property = "total",column = "total"), @Result(property = "user",column = "uid", javaType = User.class, one = @One(select = "com.itheima.mapper.UserMapper.findById")) }) List<Order> findAll(); } public interface UserMapper { @Select("select * from user where id=#{id}") User findById(int id); } 6.4.6 测试结果 @Test public void testSelectOrderAndUser() { List<Order> all = orderMapper.findAll(); for(Order order : all){ System.out.println(order); } } 6.5 一对多查询 6.5.1 一对多查询的模型用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单。
6.5.2 一对多查询的语句对应的sql语句:
select * from user; select * from orders where uid=查询出用户的id;查询的结果如下:
6.5.3 修改User实体 public class Order { private int id; private Date ordertime; private double total; //代表当前订单从属于哪一个客户 private User user; } public class User { private int id; private String username; private String password; private Date birthday; //代表当前用户具备哪些订单 private List<Order> orderList; } 6.5.4 创建UserMapper接口 List<User> findAllUserAndOrder(); 6.5.5 使用注解配置Mapper public interface UserMapper { @Select("select * from user") @Results({ @Result(id = true,property = "id",column = "id"), @Result(property = "username",column = "username"), @Result(property = "password",column = "password"), @Result(property = "birthday",column = "birthday"), @Result(property = "orderList",column = "id", javaType = List.class, many = @Many(select = "com.itheima.mapper.OrderMapper.findByUid")) }) List<User> findAllUserAndOrder(); } public interface OrderMapper { @Select("select * from orders where uid=#{uid}") List<Order> findByUid(int uid); } 6.5.6 测试结果 List<User> all = userMapper.findAllUserAndOrder(); for(User user : all){ System.out.println(user.getUsername()); List<Order> orderList = user.getOrderList(); for(Order order : orderList){ System.out.println(order); } System.out.println("-----------------------------"); } 6.6 多对多查询 6.6.1 多对多查询的模型用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用多对多查询的需求:查询用户同时查询出该用户的所有角色
6.6.2 多对多查询的语句对应的sql语句:
select * from user; select * from role r,user_role ur where r.id=ur.role_id and ur.user_id=用户的id查询的结果如下:
6.6.3 创建Role实体,修改User实体 public class User { private int id; private String username; private String password; private Date birthday; //代表当前用户具备哪些订单 private List<Order> orderList; //代表当前用户具备哪些角色 private List<Role> roleList; } public class Role { private int id; private String rolename; } 6.6.4 添加UserMapper接口方法 List<User> findAllUserAndRole(); 6.6.5 使用注解配置Mapper public interface UserMapper { @Select("select * from user") @Results({ @Result(id = true,property = "id",column = "id"), @Result(property = "username",column = "username"), @Result(property = "password",column = "password"), @Result(property = "birthday",column = "birthday"), @Result(property = "roleList",column = "id", javaType = List.class, many = @Many(select = "com.itheima.mapper.RoleMapper.findByUid")) }) List<User> findAllUserAndRole();} public interface RoleMapper { @Select("select * from role r,user_role ur where r.id=ur.role_id and ur.user_id=#{uid}") List<Role> findByUid(int uid); } 6.6.6 测试结果 UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> all = mapper.findAllUserAndRole(); for(User user : all){ System.out.println(user.getUsername()); List<Role> roleList = user.getRoleList(); for(Role role : roleList){ System.out.println(role); } System.out.println("----------------------------------"); }版权声明:本文标题:MyBatis详解 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1686987515a125808.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论