一.建立项目雏形
1.创建数据库和用户表
2.创建普通maven项目mybatis,并创建一个子模块mybatis1(当然也可以不建子模块)
3.父项目mybatis的pom.xml引入依赖,以及配置静态资源过滤,可以给子模块的pom.xml也添加静态资源过滤配置。
<dependencies>
<!-- 数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
4.配置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>
<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://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="用户名"/>
<property name="password" value="密码"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/han/dao/UserMapper.xml"/>
</mappers>
</configuration>
5.创建工具类获取sqlSession
package com.han.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class mybatisUtils {
private static SqlSessionFactory sqlSessionFactory ;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
SqlSession sqlSession=sqlSessionFactory.openSession();
return sqlSession;
}
}
6.建立实体类,建议加上tostring,否则无法查询出真实数据库数据。
package com.han.pojo;
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
7.创建UserMapper接口
package com.han.dao;
import com.han.pojo.User;
import java.util.List;
public interface UserMapper {
// 查询所有用户
List<User> getUserList();
// 根据id查询用户
User getUserById( int id);
// 增加一个用户
int addUser(User user);
// 修改用户
int updateUser(User user);
// 删除用户
int deleteUser(int id);
}
8.创建接口映射文件UserMapper.xml,当然也可以使用注解,见人见智。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.han.dao.UserMapper">
<select id="getUserList" resultType="com.han.pojo.User">
select * from user;
</select>
<select id="getUserById" parameterType="int" resultType="com.han.pojo.User">
select * from user where id=#{id};
</select>
<insert id="addUser" parameterType="com.han.pojo.User" >
insert into user(id,name,pwd) values(#{id},#{name},#{pwd});
</insert>
<update id="updateUser" parameterType="com.han.pojo.User" >
update user set name=#{name},pwd=#{pwd} where id=#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id}
</delete>
</mapper>
9.创建测试类(在test文件夹下面写测试类)
package com.han.dao;
import com.han.pojo.User;
import com.han.utils.mybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void test(){
SqlSession sqlSession= mybatisUtils.getSqlSession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
List<User> userList=userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void getUserById(){
SqlSession sqlSession= mybatisUtils.getSqlSession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
User user=userMapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
@Test
public void addUser(){
SqlSession sqlSession= mybatisUtils.getSqlSession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
int n=userMapper.addUser(new User(4,"王柳","141414"));
if(n>0){
System.out.println("插入成功");
}
// 增删改:提交事务,否则数据库没有数据。
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUser(){
SqlSession sqlSession= mybatisUtils.getSqlSession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
int n=userMapper.updateUser(new User(4,"王留","141414"));
if(n>0){
System.out.println("修改成功");
}
// 增删改:提交事务,否则数据库没有数据。
sqlSession.commit();
sqlSession.close();
}
@Test
public void deleteUser(){
SqlSession sqlSession= mybatisUtils.getSqlSession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
int n=userMapper.deleteUser(2);
if(n>0){
System.out.println("删除成功");
}
// 增删改:提交事务,否则数据库没有数据。
sqlSession.commit();
sqlSession.close();
}
}
查询结果(增删改查均可实现):
二.万能Map
这里还是通过增加一个用户说明map的作用:可以和数据库字段不一样,随意命名,可以和实体类属性个数不一样(没有数据的为null)。
// map增加用户
int addUserByMap(Map<String,Object> map);
<insert id="addUserByMap" parameterType="map" >
insert into user(id,name,pwd) values(#{user_id},#{user_name},#{user_pwd});
</insert>
@Test
public void addUserByMap(){
SqlSession sqlSession= mybatisUtils.getSqlSession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
Map<String,Object> map=new HashMap<String,Object>();
map.put("user_id",5);
map.put("user_name","薛五");
map.put("user_pwd","151515");
int n=userMapper.addUserByMap(map);
if(n>0){
System.out.println("添加成功");
}
// 增删改:提交事务,否则数据库没有数据。
sqlSession.commit();
sqlSession.close();
}
三.数据库连接信息—配置文件读取
上面mybatis的配置文件直接对数据库的连接信息作了设置。下面通过配置文件优化。resources下面新建db.properties配置文件:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=root
将mybatis核心配置文件中改回(你也可以不改,因为配置文件优先级较高):
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
在核心配置文件中引入外部数据库配置文件:
<!-- 引入外部数据库配置文件-->
<properties resource="db.properties"/>
配置文件和标签可以混合使用,但配置文件优先级较高。
四.实体类优化—别名
不设置别名的情况下,实体类默认别名为实体类类名的小写,比如User实体类的默认别名为user。
<!-- 别名-->
<typeAliases>
<typeAlias type="com.han.pojo.User" alias="User"/>
</typeAliases>
或者使用包(实体类较多时使用),mybatis会扫描包下面javabean,依然可以直接使用User。
<!-- 别名-->
<typeAliases>
<package name="com.han.pojo"/>
</typeAliases>
这样就可以在sql中不必使用绝对路径作为返回值类型,而是直接使用User。
<select id="getUserList" resultType="com.han.pojo.User">
select * from user;
</select>
<select id="getUserList" resultType="User">
select * from user;
</select>
五.设置
可以在mybatis核心配置文件中开启驼峰转换,如数据库字段为user_name,会转换为userName,这样如果实体类属性为userName,就不需要做结果集映射了。
<!-- 设置:开启驼峰转换-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
六.映射器
每一个Mapper.xml文件都需要在mybatis核心配置文件中注册。
可以通过resource、class、url、package这四种方法,一般用resource和class。比如上面的mybatis配置文件中注册UserMapper.xml时用的resource:
<mappers>
<mapper resource="com/han/dao/UserMapper.xml"/>
</mappers>
如果换成class,引入的是接口文件:
<mappers>
<mapper class="com.han.dao.UserMapper"></mapper>
</mappers>
如果使用包:
<mappers>
<package name="com.han.dao"/>
</mappers>
注意:使用包和class,UserMapper接口文件和UserMapper.xml,也就是接口文件和配置文件必须同名、必须在同一个包下,否则无法读取。而使用resource没有这个限制。
七.resultMap结果集映射
为了解决实体类属性和数据库字段不一致而生,复杂sql查询中,多对一和一对多时使用。先说一下基本用法。现在将实体类的pwd属性改为password,这样数据中的字段和实体类属性password就不对应了:
private int id;
private String name;
private String password;
测试结果显示,无法查询到用户密码。
1.通过起别名
<select id="getUserList" resultType="User">
select id,name,pwd as password from user;
</select>
2.使用resultMap映射结果
type=“User”,说明在查询时会去User实体类中找password这个属性。
<resultMap id="UserMap" type="User">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserList" resultMap="UserMap">
select * from user;
</select>
八.日志工厂
在mybatis核心配置文件中配置setting,STDOUT_LOGGING表示标准日志工厂,直接在setting中配置即可使用:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
多出来的信息就是由日志生成的。
现在我们想使用LOG4J 这种日志:
<settings>
<setting name="logImpl" value="LOG4J "/>
</settings>
这样是不够的。
第一步:需要导入依赖
<!--log4j-->
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
第二步:写配置文件
在resources下面新建log4j.properties配置文件,写入一下配置
#输出到哪里
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/han.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEGUB
log4j.logger.java.sql=DEGUB
log4j.logger.java.sql.Statement=DEGUB
log4j.logger.java.sql.ResultSet=DEGUB
log4j.logger.java.sql.PreparedStatement=DEGUB
第三步:在需要生成日志的类里面使用
这里是在测试类中测试日志:
public class UserMapperTest {
static Logger logger = Logger.getLogger(UserMapperTest.class);
@Test
public void testLog4j(){
logger.info("info:log4j的info测试信息");
logger.debug("debug:log4j的debug测试信息");
logger.error("error:log4j的error测试信息");
}
}
可以看到控制台输出相应日志信息。
左侧也生成了日志文件。
九.使用注解开发
注解开发比配置文件简单,不需要写配置文件。但无法配置resultMap这样的结果映射,也不能设置返回类型和参数类型等。这里举个例子:
// 查询所有用户
@Select("select * from user")
List<User> getUserList();
直接在接口上面使用注解写入sql即可,此时没有配置文件,mybatis核心配置文件中就只能使用class来映射接口了。
<mappers>
<mapper class="com.han.dao.UserMapper"></mapper>
</mappers>
可以看到,使用注解,没有结果映射,密码查出来为空。
十.lombok插件的使用
这个插件自动生成构造函数和get、set。
第一步安装插件
第二步:安装依赖
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
接下来就可以在实体类中使用了,大量的get、set和构造就只剩下下面这样:
package com.han.pojo;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String password;
}
十一.@Param
这是给参数加注解的,会以@Param的参数名为较高优先级。
要使用该参数注解,需要导入依赖:
<!-- @param-->
<dependency>
<groupId>org.apache.ibatis</groupId>
<artifactId>ibatis-core</artifactId>
<version>3.0</version>
</dependency>
接着在参数中使用:
// 删除用户
int deleteUser(@Param("user_id") int id);
此时xml文件中接收的参数名必须为@param里面的"user_id",否则无法接收参数。
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id}
</delete>
报错:无法接收参数id
必须改为:
<delete id="deleteUser" parameterType="int">
delete from user where id=#{user_id}
</delete>
十二.resultMap在复杂查询中的应用(多对一,多个学生关联一个老师)
1.建立数据库查询模型
学生表:
老师表:
将老师的id,设置为学生表的外键,对应学生表的tid:
2.建立实体类
学生实体类
package com.han.pojo;
import lombok.Data;
@Data
public class Student {
private int id;
private String name;
// 多对一:多个学生关联一个老师
private Teacher teacher;//注意:不是private int tid;
}
老师实体类
package com.han.pojo;
import lombok.Data;
@Data
public class Teacher {
private int id;
private String name;
}
3.接口
TeacherMapper
public interface TeacherMapper {
Teacher getTeacher(@Param("tid") int id);
}
TeacherMapper.xml
<mapper namespace="com.han.dao.TeacherMapper">
<select id="getTeacher" resultType="Teacher">
select * from teacher where id=#{tid};
</select>
</mapper>
StudentMapper
public interface StudentMapper {
public List<Student> getStudent();
}
StudentMapper.xml(方法一:resultMap子查询:按照查询嵌套处理)
<mapper namespace="com.han.dao.StudentMapper">
<!-- 第一步:查询所有学生,此时查出的老师为空,所有需要结果集映射 -->
<select id="getStudent" resultMap="StudentTeacher">
select * from student;
</select>
<!-- 第二步:映射结果集StudentTeacher-->
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!-- 老师是一个对象:association 关联-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<!-- 第三步:association 会调用这个子查询where id=#{这里其实可以随便传,会根据返回值类型去实体类Teacher中找}-->
<select id="getTeacher" resultType="Teacher">
select * from teacher where id=#{id}
</select>
</mapper>
StudentMapper.xml(方法二:resultMap联表查询:按照结果嵌套处理)
<!-- 方法二:联表查询-->
<select id="getStudent" resultMap="StudentTeacher">
select s.id sid,s.name sname,t.name tname,t.id id
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="id" column="id"/>
<result property="name" column="tname"/>
</association>
</resultMap>
测试
@Test
public void testStudent(){
SqlSession sqlSession= mybatisUtils.getSqlSession();
StudentMapper studentMapper=sqlSession.getMapper(StudentMapper.class);
List<Student> studentList=studentMapper.getStudent();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
老师属性以对象输出。
十三.resultMap在复杂查询中的应用(一对多,一个老师有多个学生)
实体类发生了改变。
学生
public class Student {
private int id;
private String name;
private int tid;
}
老师
public class Teacher {
private int id;
private String name;
// 一个老师拥有多个学生
private List<Student> students;
}
TeacherMapper
public interface TeacherMapper {
Teacher getTeacher(@Param("tid") int id);
}
TeacherMapper.xml(一对多:联表查询)
<mapper namespace="com.han.dao.TeacherMapper">
<select id="getTeacher" resultMap="TeacherStudent">
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="id" column="tid"/>
<result property="name" column="tname"/>
<!--一个老师有多个学生,students属性:collection-->
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
</mapper>
TeacherMapper.xml(一对多:子查询)
<!-- 第一步:先根据id查出老师,此时学生students属性为空-->
<select id="getTeacher" resultMap="TeacherStudent">
select * from teacher where id=#{tid};
</select>
<!-- 第二步:映射结果集-->
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudent" column="id"></collection>
</resultMap>
<!-- 第三步:collection中会调用这个子查询where tid=#{这里随便写,会根据返回值类型Student实体类中找}-->
<select id="getStudent" resultType="Student">
select * from Student where tid=#{tid}
</select>
测试结果
@Test
public void testTeacher(){
SqlSession sqlSession= mybatisUtils.getSqlSession();
TeacherMapper teacherMapper=sqlSession.getMapper(TeacherMapper.class);
Teacher teacher=teacherMapper.getTeacher(1);
System.out.println(teacher);
sqlSession.close();
}
Teacher(id=1, name=韩老师, students=[Student(id=1, name=小明, tid=1), Student(id=2, name=小红, tid=1), Student(id=3, name=小王, tid=1), Student(id=4, name=小丽, tid=1), Student(id=5, name=小李, tid=1)])
十四.动态sql
1.建立blog表
2.Blog实体类
package com.han.pojo;
import lombok.Data;
import java.util.Date;
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime; //需要在设置中开启驼峰转换,数据库字段为create_time
private int views;
}
3.IDutils类,生成随机字符串作为id(String类型)
package com.han.utils;
import java.util.UUID;
//@SuppressWarnings("all") 镇压警告
public class IDutils {
public static String getId(){
return UUID.randomUUID().toString().replaceAll("-","");
}
}
4.查询
@if
BlogMapper
public interface BlogMapper {
List<Blog> queryBlogIF(Map map); //通过map动态接收参数
}
BlogMapper.xml()
<mapper namespace="com.han.dao.BlogMapper">
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from blog
<where>
<if test="title!=null">
and title=#{title}
</if>
<if test="author!=null">
and author=#{author}
</if>
</where>
</select>
</mapper>
测试:
@Test
public void queryBlogIF(){
SqlSession sqlSession= mybatisUtils.getSqlSession();
BlogMapper blogMapper=sqlSession.getMapper(BlogMapper.class);
HashMap map=new HashMap();
map.put("author","英俊");
map.put("title","第二本书");
List<Blog> blogList=blogMapper.queryBlogIF(map);
for (Blog blog : blogList) {
System.out.println(blog);
}
}
@Choose
choose语句:只选择其中一个成立的条件执行,都不成立则执行otherwise里面的条件,从第一个开始判断,第一个成立了就不执行后面的了。加上where标签还是为了解决and的问题
List<Blog> queryBlogChoose(Map map);
<select id="queryBlogChoose" parameterType="map" resultType="Blog">
select * from blog
<where>
<choose>
<when test="title!=null">
title=#{title}
</when>
<when test="author!=null">
and author=#{author}
</when>
<otherwise>
and views=#{views}
</otherwise>
</choose>
</where>
</select>
测试
@Test
public void queryBlogChoose(){
SqlSession sqlSession= mybatisUtils.getSqlSession();
BlogMapper blogMapper=sqlSession.getMapper(BlogMapper.class);
HashMap map=new HashMap();
map.put("author","英俊");
List<Blog> blogList=blogMapper.queryBlogChoose(map);
for (Blog blog : blogList) {
System.out.println(blog);
}
}
@set(动态update)
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)
int updateBlog(Map map);
<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>
测试
@Test
public void updateBlogSet(){
SqlSession sqlSession= mybatisUtils.getSqlSession();
BlogMapper blogMapper=sqlSession.getMapper(BlogMapper.class);
HashMap map=new HashMap();
map.put("author","英俊韩");
map.put("title","第一滴血");
map.put("id","00518a44625b4288876617de7946cbbe");
blogMapper.updateBlog(map);
sqlSession.commit();
sqlSession.close();
}
@trim
通过前缀、后缀、覆盖等,解决where的and、set的’,'问题。
@sql片段
对于上面的例子,有段被多次复用的sql代码片段:
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{author}
</if>
可以通过sql标签,添加一个id:
<sql id="if-title-author">
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{author}
</if>
</sql>
在需要用到这段sql的地方,通过id引用到include标签里面:
<include refid="if-title-author">
</include>
@foreach
List<Blog> queryBlogForeach(Map map);
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="(" close=")" separator="or" >
id=#{id}
</foreach>
</where>
</select>
测试
@Test
public void updateBlogForeach(){
SqlSession sqlSession= mybatisUtils.getSqlSession();
BlogMapper blogMapper=sqlSession.getMapper(BlogMapper.class);
HashMap map=new HashMap();
ArrayList<Integer> ids=new ArrayList<Integer>(); //用于存放每条数据的id
ids.add(1);
ids.add(2);
map.put("ids",ids);
blogMapper.queryBlogForeach(map);
sqlSession.close();
}
完。