一、创建流程
1.导入依赖
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
</dependencies>
2.创建工具类MybatisUtils
//简化版,不带线程池
public class MybatisUtils {
//
private static SqlSessionFactory sqlSessionFactory;
static {
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
//此时sqlSessionFactory不要使用SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);语句,
//此时会使用重新定义的sqlSessionFactory,而未将数据注册到外部的private static SqlSessionFactory sqlSessionFactory的资源中,此时会提示空指针异常
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
public static void closeSqlSession() {
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.close();
}
```java
//带线程池
public class MybatisUtils { private static ThreadLocal<SqlSession> localSessions = new ThreadLocal<SqlSession>();
static {
InputStream inputStream = null;
String resource = "mybatis-config.xml";
try {
// 加载Mybatis的配置文件
inputStream =Resources.getResourceAsStream(resource);
// 创建SqlSessionFactory对象.
factory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
} finally {
if ( inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 获取Session对象
public static SqlSession getSession() {
SqlSession session = localSessions.get();
if (session == null) {
session = factory.openSession();
localSessions.set(session);
}
return session;
}
// 关闭session的方法
public static void closeSession() {
SqlSession session = localSessions.get();
if (session != null) {
session.close();
localSessions.remove();
}
}
}
3.创建工具类的配置文件mybatis-config.xml及可能遇到的问题
<?xml version="1.0" encoding="UTF8" ?>
<!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.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/mybatis/dao/UserMapper.xml"/>
</mappers>
</configuration>
-
若提示1字节的UTF-8序列的字节1无效的问题处理,可将
<?xml version="1.0" encoding="UTF-8" ?>改为<?xml version="1.0" encoding="UTF8" ?>
-
List item
4.创建实体类
package com.mybatis.pojo;
public class User {
private Integer id;
private String name;
private String password;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User() {
}
public User(Integer id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
}
5.创建接口UseDao
接口中指定了方法名称
package com.mybatis.dao;
import com.mybatis.pojo.User;
import java.util.List;
public interface UserDao {
List<User> getUserList();
}
6.创建接口UseDao对应的UserMapper.xml配置文件
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.mybatis.dao.UserDao">
<select id="getUserList" resultType="com.mybatis.pojo.User">
select * from mybatis.user
</select>
</mapper>
7.创建测试类
public class MybatisUtilsTest {
@Test
public void getSqlSession() {
SqlSession session = null;
try {
session = MybatisUtils.getSession();
UserDao mapper = session.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
}
}
- 若提示找不到配置文件,则在pom.xml文件中配置如下信息
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
二、模糊查询
<select id="getUserLike" resultType="com.mybatis.pojo.User">
select * from mybatis.user where name like "%"#{value}"%"
</select>
避免通配符写在参数中
@Test
public void getUserLike() {
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
//避免通配符写在参数中,下列所示
List<User> userLike = mapper.getUserLike("%add%");
for (User user : userLike) {
System.out.println(user);
}
session.close();
}
三、configuration(配置)
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
properties(属性)
方法一:直接引入外部文件
- 在 properties 元素的子元素中设置,通过database.properties文件
driver =com.mysql.cj.jdbc.Driver
url =jdbc:mysql://localhost:3306/mybatis?useSSL=true&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8
username =
password =
- 在配置文件中引入
//config.xml文件中标签顺序如下,否则会报错
//<?xml version="1.0" encoding="UTF-8"?>
//<configuration><!--配置-->
// <properties/><!--属性-->
// <settings/><!--设置-->
// <typeAliases/><!--类型别名-->
// <typeHandlers/><!--类型处理器-->
// <objectFactory/><!--对象工厂-->
// <plugins/><!--插件-->
// <environments><!--配置环境-->
// <environment><!--环境变量-->
// <transactionManager/><!--事务管理器-->
// <dataSource/><!--数据源-->
// </environment>
// </environments>
// <databaseidProvider/><!--数据库厂商标识-->
// <mappers/><!--映射器-->
//</configuration>
<configuration>
<properties resource="db.properties"></properties>
</configuration>
- 在整个配置文件中用来替换需要动态配置的属性值
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
方法二:核心配置文件中直接引入
<configuration>
<properties resource="db.properties">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="123"/>
<property name="password" value="321"/></properties>
</configuration>
注意:如果两个文件有同一个字段,优先使用后外部配置文件
settings(设置)
Column 1 | Column 2 | Column 3 | Column 4 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true /false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true/ false | false |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J / LOG4J/ LOG4J2 /JDK_LOGGING/ COMMONS_LOGGING / STDOUT_LOGGING /NO_LOGGING | 未设置 |
typeAliases(类型别名)
- 类型别名可为 Java 类型设置一个缩写名字。
- 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
方法一:在核心配置文件中引入
<typeAliases>
<typeAlias type="com.mybatis.pojo.User" alias="User"></typeAlias>
</typeAliases>
方法二:指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
<typeAliases>
<package name="com.mybatis.pojo"/>
</typeAliases>
//在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名
//若有注解,则别名为其注解值。创建实体类时:
@Alias("user")
public class User {
...
}
//com.mybatis.pojo.User别名为user
建议:
实体类较少,使用方法一,可以自定义别名
实体类较多,使用方法二,添加注解可以自定义别名
常见的 Java 类型内建的类型别名。它们都是不区分大小写的。
注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
environments(环境配置)
MyBatis 可以配置成适应多种环境,不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境.
environments 元素定义了如何配置环境。
environment(环境变量)
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<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>
注意一些关键点:
默认使用的环境 ID(比如:default="development")。
每个 environment 元素定义的环境 ID(比如:id="development")。
事务管理器的配置(比如:type="JDBC")。
数据源的配置(比如:type="POOLED")。
transactionManager(事务管理器)
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。
!!!提示:如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
dataSource(数据源)
大多数 MyBatis 应用程序会按示例中的例子来配置数据源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")。
mappers(映射器)
方法一(推荐):
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
</mappers>
方法二:
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
注意:
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须在同一包下
方法三:
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers
注意:
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须在同一包下
作用域(Scope)和生命周期
错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
- 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了
- SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
SqlSessionFactory:
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- SqlSessionFactory 的最佳作用域是应用作用域。
- 想象为数据库连接池
- 最简单的就是使用单例模式或者静态单例模式。
SqlSession:
- 连接到连接池的请求
- 每个线程都应该有它自己的 SqlSession 实例。
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 用完之后关闭,否则占用资源
四、解决属性名和字段名不一致的问题
结果映射
- resultMap 元素是 MyBatis 中最重要最强大的元素。
- ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
五、Log4j
1.导包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 核心配置文件中设置
//严格大小写和空格
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
- 日志文件级别
//UserMapperTest.class为当前类.class
Logger logger = Logger.getLogger(UserMapperTest.class);
logger.info("info");
logger.debug("debug");
logger.error("error");
六、使用注解
关于接口的理解
-
接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离
-
接口的本身反映了系统设计人员对系统的抽象理解
-
接口应有两类:
- 第一类是一个个体的抽象,它可对应一个抽象实体(abstract.class)
- 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface) -
一个个体可能有多个抽象面,抽象体与抽象面是有区别的
//使用class进行加载,且接口文件同.xml文件必须在同一个包下
<mappers>
<mapper class="com.mybatis.dao.UserMapper"></mapper>
</mappers>
关于@Param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要添加
- 如果只有一个基本类型,可以忽略,建议添加
- SQL中的引用就是@Param()中设定的属性名
public interface UserMapper {
@Select("select * from user")
List<User> getUser();
@Select("select * from user where id=#{id}")
User getUserById(@Param("id") int id);
@Insert("insert into user (id,name,password) values (#{id},#{adminName},#{adminPassword})")
int addUser(User user);
}
${}和#{}的区别
原sql语句:
delete from
ups_role_permission_dataparams
where role_id = #{roleId,jdbcType=INTEGER}
在这里用到了#{},使用#时:
1、用来传入参数,sql在解析的时候会加上" ",当成字符串来解析 ,如这里 role_id = “roleid”;
2、#{}能够很大程度上防止sql注入;
延伸:
1、用${}传入数据直接显示在生成的sql中,如上面的语句,用role_id = ${roleId,jdbcType=INTEGER},那么sql在解析的时候值为role_id = roleid,执行时会报错;
2、${}方式无法防止sql注入;
3、$一般用入传入数据库对象,比如数据库表名;
4、能用#{}时尽量用#{};
七、LomBok
- 下载插件
- 导入jar包
- 实体类上加注解
IntelliJ Lombok plugin
GitHub | Issues | Donate ( PayPal )
A plugin that adds first-class support for Project Lombok
Features
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass
@ExtensionMethod (Experimental, activate manually in plugin settings)
Lombok config system
Code inspections
Refactoring actions (lombok and delombok)
@Data
无参/get/set/euqals/hashCode/toString
@AllArgsConstructor
有参方法,会覆盖掉@Data中的无参方法
@NoArgsConstructor
单独添加无参方法
八、查询
多对一
按照查询嵌套处理
<mapper namespace="com.mybatis.dao.StudentMapper">
<resultMap id="StudentsTeacher" type="Student">
<result property="id" column="id"></result>
<result property="name" column="name"></result>
//association
<association property="teacher" column="tid" javaType="Teacher" select="getTeacherById"></association>
</resultMap>
<select id="getStudents" resultMap="StudentsTeacher">
select * from student
</select>
//此方法不用在接口中创建
<select id="getTeacherById" resultType="Teacher">
select * from teacher where id=#{id}
</select>
</mapper>
一对多
按照查询嵌套处理
<select id="getTeacher" resultType="Teacher">
select * from teacher;
</select>
<resultMap id="teacherStudent" type="Teacher">
<result property="id" column="tid"></result>
<result property="name" column="tname" ></result>
//collection
<collection property="students" ofType="Student">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<result property="tid" column="tid"></result>
</collection>
</resultMap>
<select id="getTeacherStudent" 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>
- 关联-association 多对一
- 集合-collection 一对多
- JavaType用来指定实体类中属性的类型
- ofType用来指定映射到List后者集合中的pojo类型,泛型中的约束类型
九、动态SQL
if 语句
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog where
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
HashMap<String, String> map = new HashMap<String, String>();
map.put("title","Mybatis");
map.put("author","MBP");
- 同时使用map注入参数相当于&&
- 如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询
select * from blog where title = #{title} and author = #{author}
Where标签
- “where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以 AND 或OR 开头的,则它会剔除掉。
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
- 这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。
Set标签
- 当 update 语句中没有使用 if 标签时,如果有一个参数为 null,都会导致错误。
- 当在 update 语句中使用if标签时,如果前面的if没有执行,则或导致逗号多余错误。
- set标签可以将动态的配置 SET 关键字,并剔除追加到条件末尾的任何不相关的逗号。
- 使用 if+set 标签修改后,如果某项为 null 则不进行更新,而是保持数据库原值。
<!--注意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>
trim标签
//可以代替where标签
<trim prefix="WHERE" prefixOverrides="AND | OR"></trim>
//可以代替set标签
<trim prefix="SET" suffixOverrides=","></trim>
choose语句
- choose语句类似于 Java 的 switch 语句,查询条件有一个满足即可
<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>
SQL片段
- 有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
- 提取SQL片段:
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
- 引用SQL片段:
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="if-title-author"></include>
<!-- 在这里还可以引用其他的 sql 片段 -->
</where>
</select>
- 注意:
- 最好基于 单表来定义 sql 片段,提高片段的可重用性
- 在 sql 片段中不要包括 where,一般只有if判断