软件的半成品,解决了软件开发过程中的普适性问题,从而简化了开发步骤,提供了开发的效率java
ORM(Object Relational Mapping)对象关系映射,将程序中的一个对象与表中的一行数据一一对应mysql
ORM框架提供了持久化类与表的映射关系,在运行时参照映射文件的信息,把对象持久化到数据库中git
存在大量的冗余代码github
手工建立 Connection、Statement 等web
手工将结果集封装成实体对象sql
查询效率低,没有对数据访问进行过优化(Not Cache)数据库
MyBatis本是Apache软件基金会的一个开源项目iBatis,2010年这个项目由apache software foundation 迁移到了Google Code,而且更名为MyBatis。2013年11月迁移到Github。MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎全部的 JDBC 代码以及设置参数和获取结果集的工做。MyBatis 能够经过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录apache
官方网站:http://www.mybatis.org/mybatis-3/微信
下载地址:https://github.com/mybatis/mybatis-3/releases/tag/mybatis-3.5.1session
关于对MyBatis的使用,咱们建立的Maven项目是不须要下载MyBatis的,而是能够经过在Maven项目中的pom文件中添加MyBatis依赖使用!后续我会把MyBatis的开发流程奉上!
File -> NewProject |
---|
![]() |
建立Maven项目 |
![]() |
在pom.xml文件中添加MyBatis核心依赖和MyBatis Log(日志)依赖
- 核心依赖便是使用MyBatis必备依赖环境,必须导入
- MyBatis Log(日志)依赖能够查看MyBatis启动的过程,便于咱们纠错、查Bug
注意: 记得把依赖放在<dependecies>标签内,MyBatis一定是与数据库交互的,因此也须要导入mysql驱动依赖
<!--MyBatis核心依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--日志依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--MySql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
建立名为
mybatis-config.xml
配置文件并配置如下信息注意: 目前mapper.xml默认建议存放在resources中,路径不能以/开头,后续我会补全到其余配置方法
<?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">
<!--MyBatis配置-->
<configuration>
<!--JDBC环境配置、选中默认环境-->
<environments default="MySqlDB">
<!--MySql数据库环境配置-->
<environment id="MySqlDB">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--链接池-->
<dataSource type="org.apache.ibatis.datasource.pooled.PooledDataSourceFactory">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- &转义& -->
<property name="url" value="jdbc:mysql://localhost:3306/x?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--Mapper注册-->
<mappers>
<!--注册Mapper文件的所在位置-->
<mapper resource="xxxMapper.xml"/>
</mappers>
</configuration>
建立一张表来实现对数据库的操做
create table tb_user
(
id int auto_increment
primary key,
username varchar(30) null,
password varchar(30) null,
gender char null,
birth date null
) charset = utf8;
这里我是用的Lombok插件使用注解来完成Getter、Setter方法和无参、有参构造
package com.mylifes1110.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private Boolean gender;
private Date birth;
}
定义一个Dao层接口并编写一个查询方法
注意: 由于咱们使用MyBatis框架,因此咱们不须要来建立Dao层的实现类了
package com.mylifes1110.dao;
import com.mylifes1110.bean.User;
public interface UserDao {
User selectUserById(int id);
}
MyBatis框架是使用Mapper.xml文件来将对象和sql关系映射的,因此咱们先在resources文件夹中建立一个
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">
<!--namespace:所需实现的接口全限定名-->
<mapper namespace="com.mylifes1110.dao.UserDao">
<!--id:所需重写的接口抽象方法;resultType:查询后所需返回的对象类型-->
<select id="selectUserById" resultType="com.mylifes1110.bean.User">
<!--select标签是查询标签,里面包裹着查询的sql语句,其中id = #{arg0}是id = ?的意思-->
<!--#{arg0}是指id等于方法中第一个形参,也就是id-->
select id, username, password, gender, birth from tb_user where id = #{arg0}
</select>
</mapper>
将mapper.xml注册到mybatis-config.xml核心配置中
<!--Mapper注册-->
<mappers>
<!--注册Mapper文件的所在位置-->
<mapper resource="xxxMapper.xml"/>
</mappers>
package com.mylifes1110.dao;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class UserMapperTest {
@Test
public void selectUserByIdTest() throws IOException {
// 得到读取MyBatis核心配置文件的流对象
InputStream input = Resources.getResourceAsStream("mybatis-config.xml");
// 根据流对象构建SqlSession链接对象的工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(input);
// 经过工厂得到链接对象sqlSession
SqlSession sqlSession = factory.openSession();
// 经过链接对象得到接口实现类对象
UserDao userDaoImpl = sqlSession.getMapper(UserDao.class);
// 打印结果
System.out.println(userDaoImpl.selectUserById(1));
}
}
package com.mylifes1110.dao;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class UserMapperTest {
@Test
public void selectUserByIdTest2() throws IOException {
// 得到读取MyBatis核心配置文件的流对象
InputStream input = Resources.getResourceAsStream("mybatis-config.xml");
// 根据流对象构建SqlSession链接对象的工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(input);
// 经过工厂得到链接对象sqlSession
SqlSession sqlSession = factory.openSession();
// 经过链接对象直接调用接口中的方法
Object o = sqlSession.selectOne("com.mylifes1110.dao.UserDao.selectUserById", 1);
// 打印结果
System.out.println(o);
}
}
在Maven项目中resources目录中的配置文件是不会被加载编译到classes中的,因此若是咱们要将mapper.xml文件放在resources文件夹之外的文件夹就不会被编译到,以致于咱们的MyBatis使用不了!
问题解决: 为了解决此问题,咱们能够在pom.xml文件中声明mapper.xml文件所在其余目录为resources文件夹,这样就能够被编译到classes文件夹中了!
操做: 在pom.xml文件最后追加< build >标签,以即可以将xml文件复制到classes中,并在程序运行时正确读取。
注意: 为了让properties文件也被编译到,也须要在<include>标签内加入配置文件的路径,触类旁通,若是哪一个文件须要被编译咱们就加标签便可!
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include><!-- 新添加 */表明1级目录 **/表明多级目录 -->
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include><!-- 新添加 */表明1级目录 **/表明多级目录 -->
<include>**/*.properties</include><!--添加properties文件-->
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
目前咱们使用的MyBatis是在核心配置中写死了,因此咱们必须借助jdbc.properties配置文件来加载jdbc,以下操做:
建立jdbc.properties配置文件
#jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/temp?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
修改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">
<!--MyBatis配置-->
<configuration>
<properties resource="jdbc.properties" />
<!--JDBC环境配置、选中默认环境-->
<environments default="MySqlDB">
<!--MySql数据库环境配置-->
<environment id="MySqlDB">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--链接池-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<!-- &转义& -->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--Mapper注册-->
<mappers>
<!--注册Mapper文件的所在位置-->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
关于咱们在写标签的时候,每每会写大量的resultType,可是该属性须要引入一个类的包路径,若是加入类型别名就能够省去写各类包,只写类名就能够。这样为实体类定义别名,提升书写效率
操做: <properties>标签后面加入以下内容便可使用类型别名
注意: 以下标签内添加如下配置信息,指定类别名和自动扫描包内类的别名二选一,通常是扫描包的好些,由于定义类会无限定义多个类,很麻烦
<!--定义别名二选一-->
<typeAliases>
<!--定义类的别名-->
<typeAlias type="com.mylifes1110.bean.User" alias="User" />
<!--自动扫描包,将原类名做为别名-->
<package name="com.mylifes1110.bean" />
</typeAliases>
在开始时候咱们在pom.xml文件中添加了日志依赖,可是并无使用该日志依赖。若是使用的话,须要建立一个log4j依赖的配置文件,并配置以下信息
注意: 在配置文中log4j.logger.后面的路径是dao层接口所在路径,咱们能够选择执行特定的接口来使用日志功能,也能够选择全部的dao层接口都使用日志功能,这里我将全部的dao层接口都使用了日志功能
向pom.xml文件中添加log4j依赖
<!-- log4j日志依赖 https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
建立并配置log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.com.mylifes1110.dao=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
日志信息
级别 | 描述 |
---|---|
ALL LEVEL | 打开全部日志记录开关;是最低等级的,用于打开全部日志记录。 |
DEBUG | 输出调试信息;指出细粒度信息事件对调试应用程序是很是有帮助的。 |
INFO | 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。 |
WARN | 输出警告信息;代表会出现潜在错误的情形。 |
ERROR | 输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。 |
FATAL | 输出致命错误;指出每一个严重的错误事件将会致使应用程序的退出。 |
OFF LEVEL | 关闭全部日志记录开关;是最高等级的,用于关闭全部日志记录。 |
关于注册Mapper,咱们在上面知识中使用的是单个Mapper注册。其实咱们能够选择一个包来进行注册全部Mapper.xml文件的,这就须要咱们使用扫描包内全部的Mapper.xml文件。若是咱们使用该方式扫描包内全部Mapper.xml文件,须要遵循以下规则:
- Mapper.xml文件与Dao层接口放在同一包下
- Mapper.xml文件与Dao层接口命名必须保持一致,好比:UserMapper.xml和UserMapper.java
<!--扫描包方式Mapper注册-->
<mappers>
<!--dao层路径-->
<package name="com.mylifes1110.dao"/>
</mappers>
关于单一注册Mapper是以下形式,可是将Mapper文件全都放在了resources文件夹下显得乱糟糟的,为了解决这个问题,咱们能够在resources文件夹下建立一个名为mappers的文件夹(dir)来存放大量的Mapper.xml文件。那么问题来了路径该怎么写呢?以下:
<!--Mapper注册-->
<mappers>
<!--注册Mapper文件的所在位置-->
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
注意: 在增删改操做的时候,须要使用sqlSession来提交事务以后才能够完成增删改操做
序号参数绑定便是使用
#{arg0}
、#{arg1}
等等代替参数列表中的第一个参数、第二个参数等等。该方法不推荐使用,由于参数的可读性不好!
// Dao层接口
User selectUserByUserNameAndPassword(String username, String password);
// Mapper.xml
<select id="selectUserByUserNameAndPassword1" resultType="User">
SELECT * FROM t_user
WHERE username = #{arg0} and password = #{arg1}
</select>
注解参数绑定便是使用
@Param("字段")
来代替参数列表中的对应参数,随后设置好参数在Sql语句中使用#{字段}
来取对应值。该方式推荐使用!
// Dao层接口
User selectUserByUserNameAndPassword(@Param("username") String username, @Param("password")String password);
// Mapper.xml
<select id="selectUserByUserNameAndPassword2" resultType="User">
SELECT * FROM t_user
WHERE username = #{username} and password = #{password}
</select>
Map参数绑定便是使用Map集合来封装参数,并在方法中传入一个Map集合,随后在Sql语句中使用封装的键来取值。该方式了解便可,操做繁琐,不建议使用!
// Dao层接口
User selectUserByUserNameAndPassword(String username, String password);
// 测试类建立Map集合,封装数据
Map<String, Object> map = new HashMap<>();
// 自定义key,绑定参数
map.put("username", "root");
map.put("password", "123456");
User user = userDao.selectUserByUserNameAndPassword(map);
// Mapper.xml
<select id="selectUserByUserNameAndPassword3" resultType="User">
SELECT * FROM t_user
WHERE username = #{username} and password = #{password} <!-- 经过key得到value -->
</select>
对象参数绑定便是在Dao层接口方法的参数列表中传入对象,这样在Sql语句中就能够随意取出该对象中的字段值了。由于好多场景须要传入对象,因此该方式方便简单,推荐使用!
// Dao层接口
User selectUserByUserNameAndPassword(User user);
// Mapper.xml
<select id="selectUserByUserNameAndPassword4" resultType="User">
SELECT * FROM t_user
WHERE username = #{username} and password = #{password} <!-- 对象中取出的字段值 -->
</select>
查询标签: <select id="接口方法名" resultType="返回值类型">
普通查询既是咱们使用MyBatis查询单表或多表中的数据
// Dao层接口
User selectUserByUserNameAndPassword(@Param("username") String username, @Param("password")String password);
// Mapper.xml
<select id="selectUserByUserNameAndPassword2" resultType="User">
SELECT * FROM t_user
WHERE username = #{username} and password = #{password}
</select>
可使用count(1)来查询总数据条数
// Dao层接口
long selectUserCount();
// Mapper.xml
<select id="selectUserCount" resultType="java.lang.Long">
select count(1) from tb_user
</select>
// 测试类
@Test
public void selectUserCount() throws IOException {
InputStream input = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(input);
SqlSession sqlSession = factory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
System.out.println(userDao.selectUserCount());
}
Sql中的模糊查询在MyBatis中也是可使用的,以下是使用注解参数绑定来模糊查询的username字段
注意: 查询List集合,resultType中传入的也是集中中的泛型对象(特殊)
// Dao层接口
List<User> selectUserListByUsername1(@Param("username") String username);
// Mapper.xml
<select id="selectUserListByUsername1" resultType="com.mylifes1110.bean.User">
select id, username, password, gender, birth
from tb_user
where username like concat('%',#{username},'%')
</select>
// 查询包含Z的username
@Test
public void selectUserListByUsername1() throws IOException {
InputStream input = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(input);
SqlSession sqlSession = factory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
System.out.println(userDao.selectUserListByUsername1("Z"));
}
<delete id="接口方法名" resultType="返回值类型">
注意: 在删除标签中,resultType属性是能够省略的,而IDEA快捷键生成的<delete>标签中是默认省略的!
经过id来删除一个用户信息
// Dao层接口
int deleteUserById(@Param("id") int id);
// Mapper.xml
<delete id="deleteUserById">
delete from tb_user where id = #{id}
</delete>
<update id="接口方法名" resultType="返回值类型">
注意: 在<update>标签中也是能够省略resultType属性值的
经过id来修改一个用户信息
// Dao层接口
int updateUserById(User user);
// Mapper.xml
<update id="updateUserById">
update tb_user set
username = #{username}, password = #{password}, gender = #{gender}, birth = #{birth}
where id = #{id}
</update>
<insert id="接口方法名" resultType="返回值类型">
注意: 在<insert>标签中也是能够省略resultType属性值的
新增一个用户信息
// Dao层接口
int insertUser(User user);
// Mapper.xml
<insert id="insertUser">
insert into tb_user
(username, password, gender, birth)
values
(#{username}, #{password}, #{gender}, #{birth})
</insert>
关于主键回填正在学习的你也许不知道什么意思,可是我分析一个场景,大概你就会了解了!
场景一(int类型主键): 在开发中,int类型并自增的主键也很常见,可是当你插入一条数据并无插入主键id时,Sql会帮你自动添加主键id。这就产生了一个弊端,若是咱们想插入一条数据并获得这个主键id时就须要再查一次这条数据获取主键id,显然这很麻烦。因而,主键回填便可解决此问题,当你插入一条数据时,int类型的主键id会在insert过程当中单独查询一次主键并返回该主键id,这样一个处理Sql的操做就作了两份工做,大大提升了开发效率
场景二(String类型主键): 在开发中,与场景一相同,String类型的主键一样很常见,例如:订单ID一般是一个很长的字符串。而订单ID是能够用UUID()方法和replace()方法来获取的,由于Sql中也是存在这两个方法的,因此咱们也能够在插入订单数据的时候生成订单ID填入数据库,一样也能够在插入数据后查询出该订单ID,这样也大大提升了我开发效率
<selectKey keyProperty="主键字段名" resultType="返回值类型" order="BEFORE | AFTER">
注意: 使用时,将<selectKey>标签放在<insert>标签内。after为后,before为前。意为在插入数据以前主键回填,在查润数据以后主键回填
last_insert_id()是MySQL内置的一个函数,咱们能够经过
select last_insert_id()
来查询出最后一条插入数据的整数int类型ID。经过使用<selectKey>标签指定order属性为after来保证在插入数据以后查询出该数据的ID
// 表使用的是User表
// Dao层接口
int insertUser(User user);
// Mapper.xml
<insert id="insertUser">
<selectKey keyProperty="id" resultType="int" order="AFTER">
select last_insert_id()<!--适用于整数类型自增的主键-->
</selectKey>
insert into tb_user
(username, password, gender, birth)
values
(#{username}, #{password}, #{gender}, #{birth})
</insert>
// 测试类
@Test
public void insertOrder() {
OrderDao orderDao = MyBatisUtils.getMapper(OrderDao.class);
Order order = new Order();
order.setMoney(11.1D);
order.setUserId(2);
System.out.println(orderDao.insertOrder(order));
System.out.println(order.getId());
MyBatisUtils.commit();
}
UUID()和replace()是MySQL内置的两个内置函数,咱们能够经过UUID()方法来生成一个UUID字符串,再使用replace()来替换UUID中的“-”,来生成32位的订单ID字符串,生成订单ID后经过
SELECT REPLACE(UUID(),'-','')
来查询出添加到数据库中的订单ID
// 建立订单表
create table tb_order
(
id varchar(32) not null
primary key,
money double null,
user_id int null
) charset = utf8;
// Dao层接口
int insertOrder(Order order);
// Mapper.xml
<insert id="insertOrder" parameterType="com.mylifes1110.bean.Order">
<selectKey keyProperty="id" resultType="string" order="BEFORE">
select replace(UUID(), '-', '')
</selectKey>
insert into tb_order
(id, money, user_Id)
values
(#{id}, #{money}, #{userId})
</insert>
Resource:用于得到读取配置文件的IO对象,耗费资源,建议经过IO一次性读取全部所须要的数据。
SqlSessionFactory:SqlSession工厂类,内存占用多,耗费资源,建议每一个应用只建立一个对象。
SqlSession:至关于Connection,可控制事务,应为线程私有,不被多线程共享。
将得到链接、关闭链接、提交事务、回滚事务、得到接口实现类等方法进行封装。
package com.mylifes1110.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.InputStream;
/**
* @ClassName MyBatisUtils
* @Description MyBatis工具类
* @Author Ziph
* @Date 2020/7/11
* @Since 1.8
* @Version 1.0
*/
public class MyBatisUtils {
// 得到SqlSession工厂
private static SqlSessionFactory factory;
// 建立ThreadLocal绑定当前线程中的SqlSession对象
private static final ThreadLocal<SqlSession> tl = new ThreadLocal<SqlSession>();
static {
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(is);
} catch (Exception e) {
e.printStackTrace();
}
}
// 得到链接(从tl中得到当前线程SqlSession)
private static SqlSession openSession(){
SqlSession session = tl.get();
if(session == null){
session = factory.openSession();
tl.set(session);
}
return session;
}
// 释放链接(释放当前线程中的SqlSession)
public static void closeSession(){
SqlSession session = tl.get();
session.close();
tl.remove();
}
// 提交事务(提交当前线程中的SqlSession所管理的事务)
public static void commit(){
SqlSession session = openSession();
session.commit();
closeSession();
}
// 回滚事务(回滚当前线程中的SqlSession所管理的事务)
public static void rollback(){
SqlSession session = openSession();
session.rollback();
closeSession();
}
// 得到接口实现类对象
public static <T extends Object> T getMapper(Class<T> clazz){
SqlSession session = openSession();
return session.getMapper(clazz);
}
}