mybatis-入门
一、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 configurationPUBLIC "-//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 mapperPUBLIC "-//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()) {// 你的应用逻辑代码
}