1.入门样例
1.1 建表
CREATE DATABASE mybatis;
USE mybatis;
CREATE TABLE USER(
id INT(20) NOT NULL AUTO_INCREMENT,
NAME VARCHAR(30) DEFAULT NULL,
PASSWORD VARCHAR(30) DEFAULT NULL,
PRIMARY KEY(id)
);
INSERT INTO USER VALUES(NULL,'张三','123456'),(NULL,'李四','123456'),(NULL,'王五','123456');
SELECT * FROM USER;
1.2 第一个mybatis项目
-
依赖导入
<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.4</version> </dependency> </dependencies> <!--静态资源放行(使得非resource下的配置文件也可以被打入jar/war包)--> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
-
MybatisUtil工具类,用于获得sqlsession对象
public class MybatisUtil { private static SqlSessionFactory sqlSessionFactory; static { try { String url = "mybatis-config.xml"; InputStream resourceAsStream = Resources.getResourceAsStream(url); sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
-
Mybatis的核心配置文件编写
<?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="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--useSSL是高版本才必须填写,useUnicode是指程序增加到数据库的编码是utf-8--> <!--配置每个mapper对应的xml文件,路径间用/隔开--> <mappers> <mapper resource="com/kuang/Mapper/UserMapper.xml"/> </mappers> </configuration>
-
pojo的javabean对象编写(用lombook缩减代码量)
@Data @NoArgsConstructor @AllArgsConstructor public class User { private int id; private String name; private String password; }
-
编写mapper接口
public interface UserMapper { public List<User> findAll(); }
-
编写mapper对应的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"> <!--namespace 一定为对应的mapper的全类名--> <mapper namespace="com.kuang.Mapper.UserMapper"> <!--resultType只要写类型就行了,不用写泛型(List)--> <select id="findAll" resultType="com.kuang.pojo.User"> select * from user ; </select> </mapper>
-
测试
public class UserMapperTest { @Test //第一种方式 public void test1(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> all = mapper.findAll(); for (User user : all) { System.out.println(user); } } @Test //第二种方式 public void test2(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); List<User> list = sqlSession.selectList("com.kuang.Mapper.UserMapper.findAll"); for (User user : list) { System.out.println(user); } } }
2.CRUD
2.1 xml文件中的属性设置
- 中的包名要和Dao/Mapper接口的包名一致
- :就是对应namespace的方法名
- : sql语句执行的返回值
- : 参数类型(int类型就不用加了)
2.2 正删改查实现
mapper
public interface UserMapper {
public List<User> findAll();
public User findById(int id);
public int addUser(User user);
public int updateUser(User user);
public int deleteUserById(int id);
}
mapper.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">
<!--namespace 一定为对应的mapper的全类名-->
<mapper namespace="com.kuang.Mapper.UserMapper">
<insert id="addUser" parameterType="com.kuang.pojo.User">
insert into USER
values (null ,#{name},#{password});
</insert>
<update id="updateUser">
update user
set name = #{name} ,password = #{password}
where id = #{id};
</update>
<delete id="deleteUserById">
delete
from user
where id = #{id};
</delete>
<!--resultType只要写类型就行了,不用写泛型(List)-->
<select id="findAll" resultType="com.kuang.pojo.User">
select * from user ;
</select>
<!--若参数是基本类型,则#{}中可以随便写变量名,但最好写方法参数的名称-->
<select id="findById" resultType="com.kuang.pojo.User">
select * from user where id = #{id};
</select>
</mapper>
测试–>增删改都需要提交事务
//增删改都需要提交事务
@Test
public void testAddUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(new User(0,"何骏扬","123456"));
sqlSession.commit();
sqlSession.close();
}
@Test
public void testUpdateUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateUser(new User(4,"何志刚","123456"));
sqlSession.commit();
sqlSession.close();
}
@Test
public void testDeleteUserById(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUserById(4);
sqlSession.commit();
sqlSession.close();
}
2.3 分析错误
- 标签名不要匹配错
- resource绑定mapper,需要用路径 ,例:com/kuang/Mapper/UserMapper.xml
- 程序配置文件必须符合规范
- 输出的xml中有中文乱码,在设置->编辑器->文件编码中设置
- maven静态资源导出问题
2.4 万能Map使用
使用map作为sql的参数可以实现灵活地调用属性,从而避免了只单一用pojo对象来操作数据库,可以添加其他参数
如: select * from user where id = ? and name like % ? %;
第二个参数就不在pojo实体类中,这时候可以用map解决问题
基本使用
public int addUser2(Map<String,Object> map);
<!--#{}中的内容和键名一一对应-->
<insert id="addUser2" parameterType="map">
insert into user
values (null,#{username},#{password});
</insert>
@Test
public void testMapAddUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("username","旺旺");//和xml中的内容对应
map.put("password","123456");//和xml中的内容对应
mapper.addUser2(map);
sqlSession.commit();
sqlSession.close();
}
2.5 模糊查询
- java代码实现
public List<User> findLikeName(String name);
<select id="findLikeName" resultType="com.kuang.pojo.User" parameterType="string">
select * from USER where name like #{string};
</select>
@Test
public void testFindLikeName(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> likeName = mapper.findLikeName("%王%");
for (User user : likeName) {
System.out.println(user);
}
sqlSession.close();
}
- xml文件实现
public List<User> findLikeName(String name);
<!--%必须写成"%"-->
<!--#{}将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号-->
<select id="findLikeName" resultType="com.kuang.pojo.User" parameterType="string">
select * from USER where name like "%"#{string}"%";
</select>
@Test
public void testFindLikeName(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> likeName = mapper.findLikeName("王");
for (User user : likeName) {
System.out.println(user);
}
sqlSession.close();
}
3.配置解析
3.1 核心配置文件
- mybatis-config.xml
- Mybatis 的配置文件包含了会深深影响Mybatis行为的设置和属性信息.
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
3.1 环境配置
基本上是事务管理器为JDBC,数据源类型为POOLED
<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="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
3.2 属性
- 可以导入一个外部的配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=root
<!--resource的配置文件若有多层文件夹,用'/'隔开-->
<properties resource="database.properties"/>
- 可以在中间再加上一些属性(这里面的属性名如果和配置文件里面的属性名一样,优先使用配置文件)
<properties resource="database.properties">
<property name="username" value="root"/>
</properties>
更换environment的设置
<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>
3.3 类型别名
- 每次对一个实体类进行别名映射
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
- 对一个包下的所有类进行别名映射,默认别名为该类的类名首字母小写
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
也可以用**@Alias
**来指定该类的别名(用注解加包扫描是唯一一种idea不会爆红的写法…)
@Alias("theUser")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String name;
private String password;
}
- Mybatis内置了很多别名,如
_int --> int
string --> String
- 如果是基本类型,如int ,别名为 _ + 类型 如_int --> int
- 如果是分装类,如Integer,String 为 首字母小写 如 integer --> Integer string --> String
3.4 设置
一个日志输出的日志,可以输出sql语句的执行情况
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
3.5 映射器
- 方法一
<mappers>
<mapper resource="com/kuang/Mapper/UserMapper.xml"/>
</mappers>
- 方法二
<mappers>
<mapper class="com.kuang.Mapper.UserMapper"/>
</mappers>
注意事项:
1.mapper和配置文件再同一个包下
2.mapper和配置文件同名
- 方法三(可以注册该包下所有的mapper)
<mappers>
<package name="com.kuang.Mapper"/>
</mappers>
注意事项(同上):
1.mapper和配置文件再同一个包下
2.mapper和配置文件同名
3.6 作用域和生命周期
SqlSessionFactoryBuilder:
- 一旦创建了 SqlSessionFactory,就不再需要它了
- 最佳作用域是方法作用域(也就是局部方法变量)
SqlSessionFactory:
- 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
- 最佳作用域是应用作用域(全局)
SqlSession:
- 最佳的作用域是请求或方法作用域
- 一旦使用完毕,就要关闭
4 解决属性名和字段名不一致
4.1 问题
当出现pojo实体类中属性和数据库中表不一致
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String name;
private String pwd;//不一致的属性
}
则查询会出现
User(id=1, name=张三, pwd=null)
4.2 原因
- 首先sql语句把信息从数据库中查出来,以字段名为键,数据为值存储在ResultSet中
- mybatis提供了自动映射和配置映射
- 自动映射将ResultSet中的键和pojo实体类中名称相同的成员变量相互映射
- 自动映射也可以将ResultSet中的键和HashMap的键相互映射,值相互映射
- 自动映射就是mybatis隐式配置了一个resultMap
- 配置映射可以指定字段名和成员变量映射
4.3 解决方案一:修改sql语句
<select id="findById" resultType="com.kuang.pojo.User">
select id,name,password as pwd from user where id = #{id};
</select>
4.4 解决方案二:使用ResultMap进行配置映射
<resultMap id="UserMap" type="com.kuang.pojo.User">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="password" property="pwd"/>
</resultMap>
<select id="findById" resultMap="UserMap">
select * from user where id = #{id};
</select>
由于有自动映射的原因,可以去掉可以自动映射的属性
<resultMap id="UserMap" type="com.kuang.pojo.User">
<result column="password" property="pwd"/>
</resultMap>
<select id="findById" resultMap="UserMap">
select * from user where id = #{id};
</select>
5 日志工厂
SLF4J
LOG4J
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING [默认配置]
NO_LOGGING
5.1 默认配置
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
log4j
- 导入依赖
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
- 导入配置文件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/kuang.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
- mybatis核心文件配置
<?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"/>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<package name="com.kuang.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>
<!--配置每个mapper对应的xml文件,路径间用/隔开-->
<mappers>
<mapper resource="com/kuang/Mapper/UserMapper.xml"/>
</mappers>
</configuration>
简单使用
- 用于打印控制台语句
private static Logger logger = Logger.getLogger(the_test.class);//获取对象,记得不要导错包
@Test
public void test2(){
logger.info("hello world");
logger.debug("hello world");
logger.error("hello world");
}
6 分页
-- limit分页语法: select * from user limit startIndex , pageSize;
select * from user limit 2 , 2;
-- 这表示从第3条数据开始,选2个
select * from user limit 5 , 3;
-- 这表示从第6条数据开始,选3个
用map实现分页操作
public List<User> findByLimit(Map<String,Object> map);
<select id="findByLimit" parameterType="map" resultType="com.kuang.pojo.User">
select * from user limit #{startIndex},#{pageSize};
</select>
@Test
public void text4(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> byLimit = mapper.findByLimit(map);
for (User user : byLimit) {
System.out.println(user);
}
sqlSession.close();
}
7 注解开发
7.1 基本使用
注解开发用于一些简单sql的编写,使用时绑定接口要用class的方式,所以若注解和配置混合开发的话,xml文件和mapper接口要在同一个包下
mapper
public interface UserMapper {
@Select("select * from user")
public List<User> findAll();
}
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"/>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--配置环境-->
<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>
<!--配置每个mapper对应的xml文件,路径间用/隔开-->
<mappers>
<mapper class="com.kuang.Mapper.UserMapper"/>
</mappers>
</configuration>
测试
@Test
public void test(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAll();
for (User user : all) {
System.out.println(user);
}
sqlSession.close();
}
7.2 注解使用(有多个参数)
@Param()注解作用于方法的参数上,用于给这个参数给上一个确切的名字,用于多个基本类型参数
- 基本类型的参数或者String类型,需要加上
- 引用类型不用加
- 如果只有一个基本类型或者String,可以不加
- 我们再sql中引用的是我们这里@Param("")中设置的属性名
当有多个引用类型的参数,用Map操作
@Select("select * from user where id = #{user1.id}")
public User findById(Map<String,User> map);
@Test
public void test2(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,User> map = new HashMap<>();
map.put("user1",new User(1,"221","213123"));
map.put("user2",new User(2,"123","12213"));
User byId = mapper.findById(map);
System.out.println(byId);
sqlSession.close();
}
测试可行
7.3 CRUD
可以自动提交事务
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
UserMapper
public interface UserMapper {
@Select("select * from user")
public List<User> findAll();
@Select("select * from user where id = #{id}")
public User findById(@Param("id") int id);
@Update("update user set name = #{name} , password = #{password} where id =#{id}")
public int update(User user);
@Insert("insert into user values(null,#{name},#{password})")
public int insert(User user);
@Delete("delete from user where id = #{id}")
public int delete(@Param("id") int id);
}
测试
public class theTest {
@Test
public void test1(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAll();
for (User user : all) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void test2(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User byId = mapper.findById(1);
System.out.println(byId);
sqlSession.close();
}
@Test
public void test3(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.insert(new User(1, "呵呵", "111111"));
sqlSession.close();
}
@Test
public void test4(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.update(new User(1, "旺旺", "111111"));
sqlSession.close();
}
@Test
public void test5(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.delete(7);
sqlSession.close();
}
}
8 lombook
依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
@Data//get和set,tostring,hascode,equals,默认的无参
@NoArgsConstructor//无参构造
@AllArgsConstructor//有参构造
9 多对一和一对多查询
9.1 多对一和一对多
- 多个学生对应一个老师
- 对于学生这边而言,关联,多个学生关联一个老师[多对一]
- 对于老师而言,集合,一个老师,有很多学生[一对多]
9.2 测试环境搭建
9.2.1 建表
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) 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` (`id`, `name`, `tid`) VALUES (1, '小明', 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (2, '小红', 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (3, '小张', 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (4, '小李', 1);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (5, '小王', 1);
9.2.2 pojo
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private int id;
private String name;
private Teacher teacher; //学生这里的tid换成了一个老师的实体类
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {
private int id;
private String name;
}
9.2.3 主配置文件
<?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"/>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--配置环境-->
<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>
<!--配置每个mapper对应的xml文件,路径间用/隔开-->
<mappers>
<mapper class="com.kuang.mapper.StudentMapper"/>
<mapper class="com.kuang.mapper.TeacherMapper"/>
</mappers>
</configuration>
9.2.4 mapper.java
public interface StudentMapper {
public Student findById(@Param("id") int id);
}
public interface TeacherMapper {
public Teacher findById(@Param("id") int id);
}
9.2.5 mapper.xml
<?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="com.kuang.mapper.StudentMapper">
<select id="findById" resultType="com.kuang.pojo.Student">
select * from student where id = #{id};
</select>
</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="com.kuang.mapper.TeacherMapper">
<select id="findById" resultType="com.kuang.pojo.Teacher">
select * from teacher where id = #{id};
</select>
</mapper>
9.3 多对一(查询学生)
pojo
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {
private int id;
private String name;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private int id;
private String name;
private Teacher teacher;
}
第一种方式:嵌套 Select 查询
也就是子查询
mapper.java
public List<Student> findAll1();
public Teacher findTeacherById(int tid);
mapper.xml
<!--
思路:
1. 查询所有学生信息
2. 用查询出来的学生信息,寻找对应的老师
-->
<select id="findAll1" resultMap="findMap1" >
select * from student;
</select>
<resultMap id="findMap1" type="com.kuang.pojo.Student">
<result property="name" column="name"/>
<result property="id" column="id"/>
<!--复杂的属性单独处理, 对象: association 集合: collection-->
<!--这里的column直接作为后面select的参数传入-->
<association property="teacher" column="tid" javaType="com.kuang.pojo.Teacher" select="findTeacherById"/>
</resultMap>
<select id="findTeacherById" resultType="com.kuang.pojo.Teacher">
select * from teacher where id =#{tid}
</select>
第二种方式:嵌套结果映射
也就是联表查询
mapper.java
public List<Student> findAll2();
mapper.xml
<!--
思路:
1. 直接找两张表的信息笛卡尔积
2.把字段映射到<association>中去
-->
<select id="findAll2" resultMap="findMap2">
SELECT
s.`id` sid,
s.`name` sname,
s.`tid` stid,
t.`id` tid,
t.`name` tname
FROM
student s,teacher t
WHERE
s.tid = t.id;
</select>
<resultMap id="findMap2" type="com.kuang.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="com.kuang.pojo.Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
associate使用(用于一个对象处理)
关联(association)元素处理“有一个”类型的关系。 比如,在我们的示例中,一个博客有一个用户。关联结果映射和其它类型的映射工作方式差不多。 你需要指定目标属性名以及属性的javaType
(很多时候 MyBatis 可以自己推断出来),在必要的情况下你还可以设置 JDBC 类型,如果你想覆盖获取结果值的过程,还可以设置类型处理器。
关联的不同之处是,你需要告诉 MyBatis 如何加载关联。MyBatis 有两种不同的方式加载关联:
- 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
- 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。
首先,先让我们来看看这个元素的属性。你将会发现,和普通的结果映射相比,它只在 select 和 resultMap 属性上有所不同。
关联的嵌套结果查询
属性 | 描述 |
---|---|
property |
映射到列结果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称的字段。 无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 |
javaType |
一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType |
JDBC 类型,所支持的 JDBC 类型参见这个表格之前的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能存在空值的列指定这个类型。 |
typeHandler |
我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的完全限定名,或者是类型别名。 |
关联的嵌套 Select 查询
相对于嵌套结果查询的基础上增加的属性
属性 | 描述 |
---|---|
column |
数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。 |
select |
用于加载复杂类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。 具体请参考下面的例子。注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。 |
fetchType |
可选的。有效值为 lazy 和 eager 。 指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled ,使用属性的值。 |
9.4 一对多(查询老师)
重新搭建测试环境
pojo
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private int id;
private String name;
private int tid;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {
private int id;
private String name;
private List<Student> students; //一个老师对应多个学生
}
第一种方式:嵌套 Select 查询
mapper.java
public Teacher findById2(int id);
public List<Student> findStudentByTid(int id);
mapper.xml
<select id="findById2" resultMap="findById2Map">
select * from teacher where id = #{id};
</select>
<resultMap id="findById2Map" type="com.kuang.pojo.Teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--此处javaType可以省略-->
<collection property="students" javaType="List" ofType="com.kuang.pojo.Student" column="id" select="findStudentByTid"/>
</resultMap>
<select id="findStudentByTid" resultType="com.kuang.pojo.Student">
select * from student where tid = #{id};
</select>
第二种方式:嵌套结果映射
mapper.java
public Teacher findById(int id);
mapper.xml
<select id="findById" resultMap="findByIdMap">
select
s.id sid,
s.name sname,
s.tid stid,
t.id tid,
t.name tname
from student s,teacher t where s.tid = t.id;
</select>
<resultMap id="findByIdMap" type="com.kuang.pojo.Teacher">
<result column="tid" property="id"/>
<result property="name" column="tname"/>
<collection property="students" ofType="com.kuang.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
9.5 小结
- 关联 -association 用于多对一(pojo中的成员变量是一个实体类)
- 集合 -collection 用于一对多(pojo中成员变量是一个集合或者列表)
- javaType和ofType
- javaType用于指定实体类中属性的类型
- ofType用于指定映射到List或set中的pojo类型
10 动态sql
10.1 环境搭建
- 建表
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8
- 主配置文件
<?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"/>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--配置驼峰映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<typeAlias type="com.kuang.pojo.Blog" alias="blog"/>
</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>
<!--配置每个mapper对应的xml文件,路径间用/隔开-->
<mappers>
<mapper class="com.kuang.dao.BlogMapper"/>
</mappers>
</configuration>
- pojo
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;//这里设置了驼峰映射
private int views;
}
- utils添加IdUtil
@SuppressWarnings("all") //可以抑制警告
public class IdUtil {
public static String getId(){
return UUID.randomUUID().toString().replace("-","");
}
}
- mapper和mapper.xml
public interface BlogMapper {
public int insert(Blog blog);
}
<?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="com.kuang.dao.BlogMapper">
<insert id="insert" parameterType="blog">
insert into blog
values (#{id},#{title},#{author},#{createTime},#{views});
</insert>
</mapper>
- 插入数据
public class MyTest {
@Test
public void test(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IdUtil.getId());
blog.setTitle("Mybatis如此简单");
blog.setAuthor("狂神说");
blog.setCreateTime(new Date());
blog.setViews(9999);
mapper.insert(blog);
blog.setId(IdUtil.getId());
blog.setTitle("Java如此简单");
mapper.insert(blog);
blog.setId(IdUtil.getId());
blog.setTitle("Spring如此简单");
mapper.insert(blog);
blog.setId(IdUtil.getId());
blog.setTitle("微服务如此简单");
mapper.insert(blog);
sqlSession.close();
}
}
10.2 if标签的使用
mapper
public List<Blog> findBlogIf(Map<String,Object> map);
<select id="findBlogIf" resultType="com.kuang.pojo.Blog" parameterType="map">
select * from blog where 1=1
<if test="author != null">
and author = #{author}
</if>
<if test="title != null">
and title = #{title}
</if>
</select>
其中,test中的变量是传入的参数,也就是#{}可以取出的东西
如test="author != null"
,author就是一个变量
,如果map中没有author参数就判断失败…
10.3 where标签的使用
<select id="findBlogWhere" resultType="com.kuang.pojo.Blog" parameterType="map">
select * from blog
<where>
<if test="author != null">
and author = #{author}
</if>
<if test="title != null">
and title = #{title}
</if>
</where>
</select>
其作用是动态添加where子句
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
所有可以在where标签中要拼接的sql子句前都加上and
10.4 set标签的使用
<update id="update" parameterType="map">
update blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author},
</if>
</set>
where id = #{id}
</update>
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
所有可以在set标签中要拼接的sql子句后都加,
10.5 trim标签的使用
<trim prefix="" prefixOverrides="" suffix="" suffixOverrides=""/>
prefix:前缀,若标签中有子句则加上前缀
prefixOverrides:若标签中拼接的语句的前缀是这个属性中设定的内容,则去除该内容
suffix:后缀,若标签中有子句则加上后缀
suffixOverrides:若标签中拼接的语句的后缀是这个属性中设定的内容,则去除该内容
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<trim prefix="SET" suffixOverrides=",">
...
</trim>
10.6 choose标签的使用
该标签就是switch语句
<select id="findChoose" resultType="com.kuang.pojo.Blog">
select * from blog
<where>
<choose>
<when test="title != null">and title = #{title}</when>
<when test="author != null"> and author = #{author}</when>
<otherwise></otherwise>
<!--otherwise可以不用-->
</choose>
</where>
</select>
10.7 sql和include标签的使用
<select id="findBlogWhere" resultType="com.kuang.pojo.Blog" parameterType="map">
select * from blog
<where>
<include refid="if-author-title"/>
</where>
</select>
<sql id="if-author-title">
<if test="author != null">
and author = #{author}
</if>
<if test="title != null">
and title = #{title}
</if>
</sql>
可以把一些公用的sql片段拼解,实现代码复用
10.8 forEach标签使用
可以实现in的操作
<select id="findIn" resultType="com.kuang.pojo.Blog">
select * from blog
<where>
<foreach collection="list" separator="," item="item" open="id in (" close=")" index="index">
#{item}
</foreach>
</where>
</select>
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。,(测试后,map不可以作为参数,只能map中含有list集合,用这个集合才行)
collection:集合的类型
separator:分隔符
item:每次迭代获取的值
index:每次的下标值
open:以什么开头
close:以什么结束
11 缓存
意义:减少与数据库的交互,降低资源的浪费
11.1 一级缓存(默认开启)
- 一级缓存也叫本地缓存: sqlSession
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 以后如果需要获取相同的数据,直接从缓存里面拿,没必要再去查询数据库;
测试
@Test
public void test1(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User byId = mapper.findById(1);
System.out.println(byId);
User byId2 = mapper.findById(1);
System.out.println(byId2);
}
只会查询一次,因为查询会加入缓存
缓存失效的情况
- 查询不同的东西
- 增删改操作,可能改变原来数据,所以必定刷新缓存
- 查询不同的mapper,xml
- 手动清理缓存
sqlSession.clearCache();
11.2 二级缓存
-
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
-
基于namespace级别的缓存,一个名称空间,对应一个二级缓存
-
工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话对应的一级缓存就没有了,但是会话关闭了,一级缓存中的数据被保存到二级缓存中去
- 新的会话查询信息,就可以从二级缓存中获取内容
- 不同的么mapper查询出来的数据会放在自己对应的缓存中;
在要加上二级缓存的支持的mapper.xml中添加
<cache/>
也可以
<!--刷新时间为60秒,只读,可以存储对象或引用为512个引用,清除策略-->
<cache flushInterval="60000"
readOnly="true"
size="512"
eviction="FIFO"/>
测试
@Test
public void test2(){
SqlSession sqlSession1 = MybatisUtil.getSqlSession();
SqlSession sqlSession2 = MybatisUtil.getSqlSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User byId = mapper1.findById(1);
System.out.println(byId);
sqlSession1.close();
User byId1 = mapper2.findById(1);
System.out.println(byId1);
sqlSession2.close();
}
只在数据库查询了一次