一、mybatis安装
最简单的方式是使用maven来构建项目,将下面的依赖置于pom.xml文件中:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
截止目前,最新的版本为:3.5.14
二、构建 SqlSessionFactory
mybatis应用中的核心是SqlSessionFactory,SqlSessionFactory可以创建出sqlSession对象,而sqlSession对象可以执行sql语句。
方式一:从xml中构建
完整版目录树截图如下,mybatis-config.xml是myabtis应用的核心配置,用于定义数据库连接信息等,mapper用于定义mybatis对象对外提供的功能,pojo用于接收数据库的表记录对象,resources目录下的mapper.xml文件用于实现mybatis对象对外提供的功能。
1、创建mybatis-config.xml
在resources目录下创建一个mybatis-config.xml文件,内容为:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://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"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
mybatis是操作数据库表对象的,这里先简单配置了最基本的数据库连接信息。
注意:mysql数据库需要提前安装好,数据库表也需要提前创建好。我这里有一个demo表:
CREATE TABLE `t_user` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID,自增主键',
`username` VARCHAR(50) NOT NULL COMMENT '用户名',
`password` VARCHAR(100) NOT NULL COMMENT '密码(建议存储加密后的密码)',
`gender` VARCHAR(10) DEFAULT NULL COMMENT '性别,如:男、女',
`addr` VARCHAR(255) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`) COMMENT '用户名唯一,避免重复注册'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- 初始化用户数据
INSERT INTO `t_user` (`username`, `password`, `gender`, `addr`)
VALUES
('zhangsan', 'e10adc3949ba59abbe56e057f20f883e', '男', '北京市朝阳区'),
('lisi', 'e10adc3949ba59abbe56e057f20f883e', '女', '上海市浦东新区'),
('wangwu', 'e10adc3949ba59abbe56e057f20f883e', '男', '广州市天河区');
2、创建User类
User类用于接收数据库查询出来的对象
package com.cosseen.pojo;
import lombok.Data;
@Data
public class User {
Integer id;
String username;
String password;
String gender;
String addr;
}
这里我添加了@Data注解,有了这个注解,可以省略getId, setId等函数。
这里我使用的是1.18.3版本,因此需要给pom.xml中添加如下代码:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</dependency>
3、创建Mapper接口
mapper接口主要用于封装 库表对象 对外提供的功能,比如,我把返回所有用户的查询封装成一个selectAll函数,那么调用这个函数,就相当于执行select * from t_user;
package com.cosseen.mapper;
import com.cosseen.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> selectAll();
}
4、创建UserMapper.xml文件
这个文件是mapper接口函数的具体实现,其中id属性和mapper的函数名需要对应。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cosseen.mapper.UserMapper">
<select id="selectAll" resultType="com.cosseen.pojo.User">
select * from t_user;
</select>
</mapper>
注意:
想让程序识别到这个配置,还需要给mybatis-config.xml中加一个配置,内容如下:
<mappers>
<mapper resource="com/cosseen/mapper/UserMapper.xml"/>
</mappers>
5、创建主应用类MybatisApp
代码内容如下:
package com.cosseen;
import com.cosseen.mapper.UserMapper;
import com.cosseen.pojo.User;
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;
import java.util.List;
public class MybatisApp {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();
for (User user : users) {
System.out.println(user.getUsername());
}
}
}
6、执行结果
日志打印了执行的sql语句和执行结果。
注意:
打印数据库日志,需要配置logback,pom依赖如下:
<!-- SLF4J 核心API,日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<!-- Logback 核心模块 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.11</version>
</dependency>
<!-- Logback 经典模块(包含对 SLF4J 的实现) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
logback.xml内容如下,和mybatis-config.xml放到同级目录下即可。
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 定义日志输出格式 -->
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" />
<!-- 定义日志存储路径 -->
<property name="LOG_PATH" value="./logs" />
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 控制台输出格式 -->
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 按天滚动的文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志文件路径及名称 -->
<file>${LOG_PATH}/app.log</file>
<!-- 滚动策略:按天生成日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 滚动后文件的命名格式 -->
<fileNamePattern>${LOG_PATH}/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志文件保留天数 -->
<maxHistory>30</maxHistory>
<!-- 总日志大小限制 -->
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<!-- 日志输出格式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 错误日志单独输出 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 只输出ERROR级别及以上的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 根日志配置 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
<!-- 可以为特定包配置不同的日志级别 -->
<logger name="com.cosseen" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</logger>
<!-- 关闭第三方框架的日志输出(如Spring、MyBatis等) -->
<logger name="org.springframework" level="WARN" />
<!--<logger name="org.mybatis" level="WARN" />-->
<logger name="org.mybatis" level="DEBUG" />
</configuration>
方式二、通过java代码创建
这种方式会复用方式一中的大部分类,这里只列出了差异的文件。目录树如下:
1、创建一个数据库工厂类
package com.cosseen.factory;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
import javax.sql.DataSource;
public class UserDataSourceFactory {
// 数据库连接信息(实际开发中建议通过配置文件或配置中心读取)
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=UTC";
private static final String USERNAME = "root";
private static final String PASSWORD = "123456";
/**
* 获取 mybatis内置连接池数据源(生产环境推荐)
*/
public static DataSource getBlogDataSource() {
// 配置 HikariCP 连接池参数
PooledDataSource pooledDataSource = new PooledDataSource(DRIVER, URL, USERNAME, PASSWORD);
return pooledDataSource;
}
}
2、创建主应用类MybatisDatasourceApp
package com.cosseen;
import com.cosseen.factory.UserDataSourceFactory;
import com.cosseen.mapper.UserMapper;
import com.cosseen.pojo.User;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import javax.sql.DataSource;
import java.util.List;
public class MybatisDatasourceApp {
public static void main(String[] args) {
DataSource dataSource = UserDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(UserMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();
for (User user : users) {
System.out.println(user.getUsername());
}
}
}
三、SqlSession直接调用Mapper方法
代码如下:
package com.cosseen;
import com.cosseen.pojo.User;
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;
import java.util.List;
public class MybatisSqlsessionApp {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
List<User> users = null;
try (SqlSession session = sqlSessionFactory.openSession()) {
users = session.selectList("com.cosseen.mapper.UserMapper.selectAll", null);
}
for (User user : users) {
System.out.println(user.getUsername());
}
}
}
session对象直接调用了Mapper方法中的函数,因为selectAll没有参数,所以传递的是null。
四、命名空间
在UserMapper.xml配置文件中,有一个namespace属性,值是com.cosseen.mapper.UserMapper, 这个值有什么用呢?
<mapper namespace="com.cosseen.mapper.UserMapper">
<select id="selectAll" resultType="com.cosseen.pojo.User">
select * from t_user;
</select>
</mapper>
思考这么一个问题:
假设我还有一个Teacher表,里面也有一个selectAll方法,那程序如何区分调用的是哪个对象的selectAll方法呢? namespace的作用就是隔离不同对象的相同方法,避免混淆。
五、使用注解来完成映射
上面的UserMapper.xml文件以不写,把sql语句写入到UserMapper.java文件中。
package com.cosseen.mapper;
import com.cosseen.pojo.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
@Select("select * from t_user")
List<User> selectAll();
}
六、作用域和生命周期
- SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量),该对象不需要始终存在,一旦创建SqlSessionFactory,就不再需要它了。
SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,且只应该存在一份,所以,该对象最好是单例模式。
- SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。
下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}