文章目录
Day 5
2019年7月24日。
这是我学习数据库的第五天。
这一天,我学到了以下的知识。
分页
Mysql的分页语句
select * from user limit #{startIndex},#{pageSize};
# startIndex : 起始位置 ,默认是0开始
# pageSize :页面大小
# 如何计算出当前页面
currentPage = (currentPage-1)* pageSize
使用Limit实现分页
-
编写dao接口,代码如下:
//查询全部用户实现分页 List<User> selectUserByLimit(Map<String,Integer> map);
-
编写对应mapper文件的方法【参数可以使用map进行封装,方便参数传递】,代码如下:
<select id="selectUserByLimit" parameterType="Map" resultType="User"> select * from mybatis.user limit #{startIndex},#{pageSize} </select>
-
测试【模拟分页数据:currentPage,pageSize】,代码如下:
@Test public void selectUserByLimit(){ //创建sqlSession SqlSessionFactory sqlSessionFactory = MyBatisUtils.getSqlSessionFactory(); SqlSession sqlSession = sqlSessionFactory.openSession(); //准备数据 int currentPage = 2;//当前是第几页 int pageSize = 2; //页面大小 Map<String, Integer> map = new HashMap<String, Integer>(); map.put("startIndex",(currentPage-1)*pageSize); map.put("pageSize",pageSize); //测试 UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> users = mapper.selectUserByLimit(map); for (User user : users) { System.out.println(user); } sqlSession.close();//关闭连接 }
使用RowBounds实现分页
-
编写dao接口,代码如下:
//查询全部用户实现分页使用RowBounds List<User> selectUserByRowBounds();
-
编写对应mapper文件的方法,代码如下:
<select id="selectUserByRowBounds" resultType="User"> select * from mybatis.user </select>
-
测试【模拟分页数据:currentPage,pageSize】,代码如下:
@Test public void selectUserByRowBounds(){ //创建sqlSession SqlSessionFactory sqlSessionFactory = MyBatisUtils.getSqlSessionFactory(); SqlSession sqlSession = sqlSessionFactory.openSession(); int currentPage = 2; //当前页 int pageSize = 2; //页面大小 RowBounds rowBounds = new RowBounds((currentPage - 1) * pageSize, pageSize); //注意点;使用RowBounds就不能使用getMapper了 //selectList: 接收一个List //selectMap: 接收一个Map //selectOne : 接收只有一个对象的时候 List<User> users = sqlSession.selectList("dao.UserDao.selectUserByRowBounds", null, rowBounds); for (User user : users) { System.out.println(user); } }
Limit和RowBounds的区别
- rowBounds本质就是封装了limit
- limit是在SQL层面实现分页
- rowBounds是在代码层面实现分页
使用注解开发
面向接口编程
提出的原因:解耦,方便拓展,提高代码的复用性,上层不用管下层实现,只用去调用对应的接口即可,规范性好
- 面向过程编程:随着系统越来越大,无法使用面向过程的思想满足,只能使用面向对象编程
- 面向对象编程:所有的系统耦合性高,所有的功能都是由许许多多不同的对象去完成的
- 面向接口编程:约束开发人员操作,而且方便扩展以及规划,实现更深层次的分离(定义与实现的一个分离)
接口 & 抽象类
接口可以反映一个开发人员的水平高低,以及对系统架构的理解
简介
早期的mybatis都是使用xml进行配置,直到注解的出现,注解可以替代一些xml中的配置
不需要xml配置!
CRUD的注解
- @insert():增
- @delete():删
- @update():改
- @select():查
优化项目
事务优化
mybatis开发人员也想到了。有一个构造器,可以实现事务自动提交,代码如下:
openSession(true); // openSession构造器如果参数为true,就会自动提交事务,不用再使用commit()方法区提交
别名优化
为pojo包下的实体类(User)自动设置别名,代码如下:
<!--配置别名-->
<typeAliases>
<!--<typeAlias type="pojo.User" alias="User"/>-->
<package name="pojo"/>
</typeAliases>
工具类优化
自动提交事务,代码如下:
//获得一个带事务自动提交功能的SqlSession公共的方法
public static SqlSession getSqlSession(){
//自动提交事务
return sqlSessionFactory.openSession(true);
}
使用注解进行开发
-
在dao包下编写接口UserDao,代码如下:
import org.apache.ibatis.annotations.*; import java.util.List; public interface UserDao { //查询全部用户 @Select("select * from user") List<User> getUserList(); //通过ID查询用户 @Select("select * from user where id = #{id}") User getUserById(@Param("id") int id); //添加用户 @Insert("insert into user(id,name,pwd) values (#{id},#{name},#{pwd})") int addUser(User user); //修改用户信息 @Update("update user set name = #{name}, pwd = #{pwd} where id = #{id}") int updateUser(User user); //删除用户 @Delete("delete from user where id =#{uid}") int deleteUser(@Param("uid") int id); }
-
在resources编写mybatis的配置文件mybatis-config.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> <!--配置文件修改--> <properties resource="database.properties"/> <!--Mybatis设置--> <settings> <!--默认日志实现--> <!--<setting name="logImpl" value="STDOUT_LOGGING"/>--> <!--Log4j实现--> <setting name="logImpl" value="LOG4J"/> </settings> <!--配置别名--> <typeAliases> <!--<typeAlias type="com.kuang.pojo.User" alias="User"/>--> <package name="pojo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <!--class对应的是一个接口类--> <!--resource对应的是一个接口类的映射文件--> <mapper class="dao.UserDao"/> </mappers> </configuration>
-
在Test文件夹下编写测试类,代码如下:
import org.apache.ibatis.session.SqlSession; import org.junit.Test; import sun.rmi.server.UnicastServerRef; import java.util.List; public class UserDaoTest { @Test public void getUserList(){ SqlSession sqlSession = MyBatisUtils.getSqlSession();//带自动提交事务 UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> userList = mapper.getUserList(); for (User user : userList) { System.out.println(user); } sqlSession.close();//关闭sqlSession; } @Test public void getUserById(){ SqlSession sqlSession = MyBatisUtils.getSqlSession();//带自动提交事务 UserDao mapper = sqlSession.getMapper(UserDao.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close();//关闭sqlSession; } @Test public void addUser(){ SqlSession sqlSession = MyBatisUtils.getSqlSession();//带自动提交事务 UserDao mapper = sqlSession.getMapper(UserDao.class); User user = new User(5, "阿猫", "like-dog"); int i = mapper.addUser(user); System.out.println(i); sqlSession.close();//关闭sqlSession; } @Test public void updateUser(){ SqlSession sqlSession = MyBatisUtils.getSqlSession();//带自动提交事务 UserDao mapper = sqlSession.getMapper(UserDao.class); User user = new User(5, "阿狗", "like-cat"); int i = mapper.updateUser(user); System.out.println(i); sqlSession.close();//关闭sqlSession; } @Test public void deleteUser(){ SqlSession sqlSession = MyBatisUtils.getSqlSession();//带自动提交事务 UserDao mapper = sqlSession.getMapper(UserDao.class); int i = mapper.deleteUser(5); System.out.println(i); sqlSession.close();//关闭sqlSession; } }
【注意事项】
- 需要开启事务自动提交
- @param参数尽量都写上,如果有多个参数,就必须填写
映射
首先掌握两个单词:
- association:联系,关联,多个人可以关联到一个人
- collection:集合,一个人有一个集合,包含多个人
多对一处理
多个对象对应一个对象
比如:你们都是我的学生,多个学生对应一个老师
处理方案
- 处理方式一
使用数据库的思想处理:连表查询
-
定义dao接口,代码如下:
List<Student> getStudents();
-
编写查询语句,步骤如下:
- 查询学生信息 id name tid , 由于我们要得到老师的信息,我们需要联表查询
- 查询老师的信息 id name
<!--遇到问题:学生类中关联老师: 多个学生对应一个老师 --> <!--<select id="getStudents" resultType="Student">--> <!--select s.id,s.name,t.name from mybatis.student as s,mybatis.teacher as t--> <!--where s.tid = t.id--> <!--</select>--> <!--解决问题方式一:按查询结果嵌套处理,模拟数据库思想; --> <select id="getStudents" resultMap="StudentTeacher"> select * from mybatis.student </select> <resultMap id="StudentTeacher" type="Student"> <id column="id" property="id"/> <result column="name" property="name"/> <!--属性和字段对应 , 类和表对应 , 对象和记录 关联一个字段 需求:拿到老师这个类的属性 association : 关联,多对一 column : 数据库对应的列名 property : 对应属性名 javaType : 多对一字段对应的Java类型 select : 关联一个语句 --> <association column="tid" property="teacher" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="Teacher"> select * from mybatis.teacher where id = #{id} </select>
-
编写测试类,代码如下:
@Test public void getStudents(){ SqlSession sqlSession = MyBatisUtils.getSqlSession(); StudentDao mapper = sqlSession.getMapper(StudentDao.class); List<Student> students = mapper.getStudents(); for (Student student : students) { System.out.println("学生姓名:"+student.getName()+"\t老师姓名:"+student.getTeacher().getName()); } }
- 处理方式二
使用面向对象的思想处理:关联对象
-
定义dao接口,代码如下:
List<Student> getStudentsTwo();
-
编写处理的mapper,步骤如下:
- 查询学生id,学生姓名,老师姓名,需要从学生表和老师表中查询
- 学生对应的类进行映射,发现老师一个对象 , 所以关联一个对象;
<!-- 解决方式二:一个resultMap解决 , 模拟面向对象的思想--> <select id="getStudentsTwo" resultMap="StudentTeacher2"> select s.id,s.name,t.name as tname from mybatis.student as s, mybatis.teacher as t where s.tid = t.id </select> <!--设置结果集映射ResultMap --> <resultMap id="StudentTeacher2" type="Student"> <id property="id" column="id"/> <result property="name" column="name"/> <!--直接关联一个老师--> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association> </resultMap>
-
编写测试类,代码如下:
@Test public void getStudentsTwo(){ SqlSession sqlSession = MyBatisUtils.getSqlSession(); StudentDao mapper = sqlSession.getMapper(StudentDao.class); List<Student> students = mapper.getStudentsTwo(); for (Student student : students) { System.out.println("学生姓名:"+student.getName()+"\t老师姓名:"+student.getTeacher().getName()); } }
一对多处理
一个老师对应多个学生
一对多的业务:使用connection集合
环境搭建
一个老师对应多个学生,所以要编写实体类,代码如下:
public class Teacher {
private int id;
private String name;
//一个老师对应对个学生
private List<Student> students;
}
代码编写
-
编写dao接口,代码如下:
public interface TeacherDao { //获得一个老师下的所有学生信息; 老师是包含学生的集合; Teacher getTeacher(int id); Teacher getTeacherTwo(int id); }
-
编写对应的mapper文件,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="dao.TeacherDao"> <!--一对多的处理--> <!--面向对象方式解决--> <select id="getTeacher" resultMap="TeacherStudent"> select s.name sname,s.id sid,t.id tid, t.name tname from mybatis.student as s,mybatis.teacher as t where s.tid = t.id and t.id = #{id} </select> <resultMap id="TeacherStudent" type="Teacher"> <result property="name" column="tname"/> <collection property="students" ofType="Student"> <id property="id" column="sid"/> <result property="name" column="sname"/> </collection> </resultMap> <!--数据库思想--> <select id="getTeacherTwo" resultMap="TeacherStudent2"> select * from mybatis.teacher where id = #{id} </select> <resultMap id="TeacherStudent2" type="Teacher"> <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="T2"/> </resultMap> <select id="T2" resultType="Student"> select * from mybatis.student where tid = #{id} </select> </mapper>
-
编写测试类,代码如下:
import org.apache.ibatis.session.SqlSession; import org.junit.Test; public class TeacherDaoTest { @Test public void getTeacher(){ SqlSession sqlSession = MyBatisUtils.getSqlSession(); TeacherDao mapper = sqlSession.getMapper(TeacherDao.class); Teacher teacher = mapper.getTeacher(1); System.out.println(teacher.getName()); System.out.println(teacher.getStudents()); } @Test public void getTeacherTwo(){ SqlSession sqlSession = MyBatisUtils.getSqlSession(); TeacherDao mapper = sqlSession.getMapper(TeacherDao.class); Teacher teacher = mapper.getTeacherTwo(1); System.out.println(teacher.getName()); System.out.println(teacher.getStudents()); } }
总结
多对一:association,关联
一对多:collection,集合
两种解决方式:
- 面对对象的思想:关联对象
- SQL语句思想:联表查询
动态SQL & 缓存
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。
动态SQL就是指根据不同查询条件,生成不同的SQL语句
在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
if
参数test:里面的表达式如果为ture则执行,否则不执行,代码如下:
if(title != null)
<if test="title != null">
AND title like #{title}
</if>
choose
有时我们不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句,代码如下:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
switch (exp){
case 1:
break;
case 2:
break;
}
</select>
trim[where,set]
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
<trim prefix="SET" suffixOverrides=",">
...
</trim>
foreach
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
代码测试
测试模糊查询
- 接口编写,代码如下:
//模糊查询,可以通过自定义条件查询
List<User> getUserByLike(Map<String,Object> map);
- 映射文件编写,代码如下:
<select id="getUser" resultType="User">
select * from mybatis.user
</select>
<select id="getUserByLike" resultType="User" parameterType="Map">
select * from mybatis.user
<where>
<if test="name!=null">
name like CONCAT('%',#{name},'%')
</if>
<if test="id!=null">
and id = #{id}
</if>
</where>
</select>
- 测试类编写,代码如下:
@Test
public void getUserByLike(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map<String,Object> map = new HashMap<String,Object>();
map.put("name","秦");
map.put("id",1);
List<User> users = mapper.getUserByLike(map);
for (User user : users) {
System.out.println(user);
}
}
注意点:太过复杂的逻辑不建议使用动态SQL,简单的话可以直接使用动态SQL实现;
缓存
如果开启缓存,相关的SQL语句就会存储在缓存中,减少数据库交互操作的时间
在mapper映射文件中,添加一个标签,代码如下:
<!--开启缓存-->
<cache/>
如果要CRUD操作要查询结果需要缓存,可以使用usrCache;代码如下:
<!--
useCache: 是否开启缓存
使用缓存可以解决问题:
查询出来的结果暂时保存着,消耗内存资源;
如果短时间查询同样的语句比较多,可以提高速度;
-->
<select id="getUser" resultType="User" useCache="true">
select * from mybatis.user
</select>