一、日志
1.日志工厂
如果一个数据库操作出现了异常,我们需要排错。
STDOUT_LOGGING标准日志输出
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
设置即可使用。这样在执行时即可打印日志。
2.LOG4J
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
- maven中导入包
- 写配置文件
log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/dlc.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
- 配置log4j为日志实现
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
log4j的使用
- 导入包(别导错了)
- 日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserMapperTest.class);
- 使用
@Test
public void testLog4j(){
logger.info("info:进入了testLog4j");
logger.debug("debug:进入了testLog4j");
logger.error("error:进入了testLog4j");
}
二、分页
为什么要分页?
- 减少数据的处理量
1.limit
select * from user limit startIndex,pageSize;
使用MyBatis实现分页
//接口
List<User> getUserByLimit(Map<String,Integer> map);
实现
<select id="getUserByLimit" parameterType="map" resultType="User">
select * from user limit #{startIndex},#{pageSize}
</select>
测试
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
HashMap<String,Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> users = userMapper.getUserByLimit(map);
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
2.RowBounds(不推荐)
3.分页插件 PageHelper
三、使用注解开发
1.面向接口编程
真正的开发中,大多都会选择面向接口编程
根本原因:解耦,可拓展,提高复用。分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好。
关于接口的理解
- 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
- 接口的本身反映了系统设计人员对系统的抽象理解。
- 接口应有两类:第一类是对一个体的抽象,它可对应为一个抽象体(abstract class);
- 第二类是对一个体某一方面的抽象,即形成一个抽象面(interface);
一个体有可能有多个抽象面。
2.使用注解开发
//删除
@Delete("delete from user where id=#{id}")
int deleteUser(int id);
在配置文件中绑定接口:
<mappers>
<mapper class="com.dlc.dao.UserMapper"/>
</mappers>
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
本质:反射机制
底层:动态代理
3.使用注解完成CRUD
在工具类中,将openSession方法的参数设为true,可以实现自动提交事务
public static SqlSession getSqlSession(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
return sqlSession;
}
查
//根据id查询用户
@Select("select * from user where id=#{id}")
User getUserById(@Param("id") int id);
sql语句中的参数名在@Param
中取。
至于其他的语句,都是一样的,不再列举。
关于@Param
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议加上
四、Lombok
自动生成get、set、toString、equals等方法,不用手动编写
- 在idea中安装Lombok插件
- 导入lombok依赖
@Data
get set toString hashcode equals
@AllArgsConstructor
有参构造
@NoArgsConstructor
无参构造
五、多对一处理
多对一:多个学生对应一个老师 关联
一对多:一个老师拥有多个学生 集合
数据库表准备
create table teacher(
id int(10) not null primary key,
name varchar(30) default null
)engine=innodb default charset=utf8;
insert into teacher(id,name) values(1,"秦老师");
create table student(
id int(10) not null,
name varchar(30) default null,
tid int(10) default null,
primary key(id),
key fktid(tid),
constraint fktid foreign key(tid) references teacher(id)
)engine=innodb default charset=utf8;
insert into student values (1,"小明",1);
insert into student values (2,"小红",1);
insert into student values (3,"小张",1);
insert into student values (4,"小李",1);
insert into student values (5,"小王",1);
搭建好环境
将Mapper.xml放在resource下时,要保证包名和java包下的相同,并且使用class的方式在mybatis-config.xml
中注册。
<mappers>
<mapper class="com.dlc.dao.TeacherMapper"/>
<mapper class="com.dlc.dao.StudentMapper"/>
</mappers>
学生类:
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
老师类
@Data
public class Teacher {
private int id;
private String name;
}
多对一的处理
(1)按照查询嵌套处理
查询所有学生信息以及对应的老师信息。
思路:
- 查询所有学生信息
- 根据学生的
tid
寻找对应的老师 - 写一个
resultMap
将Student与Teacher关联起来。
<mapper namespace="com.dlc.dao.StudentMapper">
<resultMap id="StudentTeacher" type="Student">
<result property="name" column="name"/>
<result property="id" column="id"/>
<!--复杂的对象需要单独处理
对象:association
集合:collection
-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher" parameterType="int">
select * from teacher where id=#{id}
</select>
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
(2)按照结果嵌套处理(更加简单)
<mapper namespace="com.dlc.dao.StudentMapper">
<select id="getStudent" resultMap="StudentTeacher">
select s.id sid,s.name sname,t.name tname
from student s,teacher t
where s.tid=t.id
</select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
</mapper>
六、一对多处理
学生类:
@Data
public class Student {
private int id;
private String name;
private int tid;
}
老师类:
@Data
public class Teacher {
private int id;
private String name;
//一个老师有多个学生
private List<Student> students;
}
1.按照结果嵌套(推荐)
<!-- 按结果嵌套查询-->
<select id="getTeacherById" resultMap="TeacherStudent" parameterType="int">
select s.id sid,s.name sname,t.name tname,t.id tid
from student s,teacher t
where s.tid=t.id and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="name" column="tname"/>
<result property="id" column="tid"/>
<!--返回一个List,使用ofType-->
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
按照查询嵌套(子查询)
<select id="getTeacherById" resultMap="TeacherStudent">
select * from teacher where id = #{id}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from student where tid=#{tid}
</select>
小结
- 关联-association
- 集合-collection
- javaType $ ofType
- javaType 用来指定实体类中属性的类型
- ofType 用来指定映射到List或者集合中的pojo类型
七、动态SQL
什么是动态SQL:
- 根据不同的条件生成不同的sql语句。
就是在拼接SQL语句,在保证SQL的正确性前提下,按照SQL格式,去组合
搭建环境
数据库表准备
create table blog(
id varchar(50) not null,
title varchar(100) not null,
author varchar(30) not null,
create_time datetime not null,
views int(30) not null
)engine=innodb default charset=utf8;
实体类:
@Data
public class Blog {
private String id;
private String title;
private String author;
//需要在配置文件中开启驼峰映射
private Date createTime;
private int views;
}
插入数据:
public void insertData(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
mapper.addBook(new Blog(IDUtils.getId(),"MyBatis如此简单","欻欻",new Date(),77));
mapper.addBook(new Blog(IDUtils.getId(),"Java如此简单","欻欻",new Date(),99));
mapper.addBook(new Blog(IDUtils.getId(),"Spring如此简单","欻欻",new Date(),99));
mapper.addBook(new Blog(IDUtils.getId(),"MySQL如此简单","欻欻",new Date(),100));
mapper.addBook(new Blog(IDUtils.getId(),"MyBatis如此简单","丁丁",new Date(),77));
mapper.addBook(new Blog(IDUtils.getId(),"Java如此简单","丁丁",new Date(),99));
mapper.addBook(new Blog(IDUtils.getId(),"Spring如此简单","丁丁",new Date(),99));
mapper.addBook(new Blog(IDUtils.getId(),"MySQL如此简单","丁丁",new Date(),100));
session.close();
}
1.if语句
假如要实现一个功能,当传入标题按照标题模糊搜索,传入作者则按照作者模糊搜索,都传入就按照他们进行模糊搜索,否则全部查出。
接口:
/**
* 当传入title,根据title查询博客,传入author根据author查询博客,什么都不传查询全部博客
* @param map
* @return
*/
List<Blog> queryBlogIF(Map map);
实现:
<select id="queryBlogIF" resultType="com.dlc.pojo.Blog" parameterType="map">
select * from blog where 1=1
<if test="title != null">
and title like "%"#{title}"%"
</if>
<if test="author != null">
and author like "%"#{author}"%"
</if>
</select>
测试类:
@Test
public void queryData(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String,Object> map = new HashMap<String, Object>();
map.put("title","MySQL");
map.put("author","欻欻");
List<Blog> blogs = mapper.queryBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
session.close();
}
//Blog(id=076fc171dbf242f380d4e471902f58bc, title=MySQL如此简单, author=欻欻, createTime=Tue Apr 07 10:32:24 CST 2020, views=100)
2.choose,when,otherwise
像是java中的switch,case,default。
假如要实现功能,要么按标题模糊搜索,要么按作者模糊搜索,要么全部查出。
接口:
List<Blog> queryBlogChoose(Map map);
实现:
<select id="queryBlogChoose" resultType="com.dlc.pojo.Blog">
select * from blog where 1=1
<choose>
<when test="title!=null">
and title like "%"#{title}"%"
</when>
<when test="author!=null">
and author like "%"#{author}"%"
</when>
</choose>
</select>
测试类:
@Test
public void queryData(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String,Object> map = new HashMap<String, Object>();
map.put("title","MySQL");
map.put("author","大大");
List<Blog> blogs = mapper.queryBlogChoose(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
session.close();
//Blog(id=076fc171dbf242f380d4e471902f58bc, title=MySQL如此简单, author=欻欻, createTime=Tue Apr 07 10:32:24 CST 2020, views=100)
//Blog(id=b779b5485e774e4186f4b543fc9e87dd, title=MySQL如此简单, author=丁丁, createTime=Tue Apr 07 11:22:03 CST 2020, views=100)
}
可以看到,当title传入后,便不会按照传入的author进行模糊搜索。
3.trim、where、set
尽管上述问题已经得到解决,但是总不能实际开发也要为每个sql语句添加where 1=1
,如果放到动态条件内又会出现错误,所以,需要使用到<where>
标签。
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
所以,上题中的实现可以写做:
<select id="queryBlogChoose" resultType="com.dlc.pojo.Blog">
select * from blog
<where>
<choose>
<when test="title!=null">
and title like "%"#{title}"%"
</when>
<when test="author!=null">
and author like "%"#{author}"%"
</when>
</choose>
</where>
</select>
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title!=null">
title = #{title},
</if>
<if test="author!=null">
author = #{author},
</if>
</set>
where id = #{id}
</update>
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
set的等价trim
<trim prefix="SET" suffixOverrides=",">
...
</trim>
4.sql片段
对于公用的(重复的)sql语句,可以抽取出来。
例如,使用<sql>
标签抽取出片段:
<sql id="if-query">
<if test="title != null">
and title like "%"#{title}"%"
</if>
<if test="author != null">
and author like "%"#{author}"%"
</if>
</sql>
在其他地方通过<include>
标签使用该片段
<select id="queryBlogIF" resultType="com.dlc.pojo.Blog" parameterType="map">
select * from blog
<where>
<include refid="if-query"/>
</where>
</select>
注意:
- 基于单表来定义SQL片段
- 不要存在where标签,影响复用
5.foreach
foreach 元素的功能非常强大,
- 它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。
- 它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
<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>