mybatis 简记
概念
mybatis 一种“半自动化”的ORM(Object Relational Mapping,对象关系映射)实现。需要手动配置映射文件以实现数据库中的数据和java对象间的映射。
参考文章 mybatis 框架
************************* 简单项目 ***************************
maven配置 - pom.xml
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version> <!-- 注意版本 -->
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>
mybatis配置 - mybatis.cfg.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="config.properties"> <!-- 引入外部资源配置文件,如果没有,resource项可以去掉 -->
<property name="password" value="1234"/> <!-- 自定义属性 -->
</properties>
<typeAliases>
<!--<typeAlias type="com.shiyanlou.mybatis.entity.User" alias="User"/>--> <!-- 单独定义某类的别名 -->
<package name="com.shiyanlou.mybatis.entity"/> <!-- 包内所有类名作为该类的别名 -->
</typeAliases>
<!-- 配置mybatis运行环境 -->
<environments default="development">
<environment id="development">
<!-- type="JDBC" 代表直接使用 JDBC 的提交和回滚设置 -->
<transactionManager type="JDBC" />
<!-- POOLED 表示支持JDBC数据源连接池 -->
<!-- 数据库连接池,由 Mybatis 管理,数据库名是 mybatis,MySQL 用户名 root,密码为空 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" /> <!-- 引用外部配置中的变量 -->
<property name="username" value="root" />
<property name="password" value="${password}" /> <!-- 引用上述自定义属性中的变量 -->
</dataSource>
</environment>
</environments>
<mappers>
<!--<mapper class="com.shiyanlou.mybatis.mapper.UserMapper"/>--> <!-- 单独指定定义某个映射类 -->
<package name="com.shiyanlou.mybatis.mapper"/> <!-- 定义该包类所有的类均为映射类 -->
</mappers>
</configuration>
外部配置文件 - config.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis_test?serverTimezone=GMT%2B8
username=root
#password=1234
日志配置 - log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# 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
实体类 - User.java
public class User {
private Integer id;
private String username;
private String password;
private String sex;
private Stringaddress;
// 此处省略 getter 和 setter
}
映射类 - UserMapper.java
public interface UserMapper {
/*
* 新增用戶
* @param user
* @return
*/
int insertUser(User user);
/*
* 修改用戶
* @param user
* @param id
* @return
*/
int updateUser(User user);
/*
* 刪除用戶
* @param id
* @return
*/
int deleteUser(Integer id);
/*
* 根据id查询用户信息
* @param id
* @return
*/
User selectUserById(Integer id);
/*
* 查询所有的用户信息
* 不建议使用,没有分页,数据量过大时,造成内存浪费,基本没有使用场景
* @return
*/
List<User> selectAllUser();
}
映射xml文件 - UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shiyanlou.mybatis.mapper.UserMapper">
<!-- 自定义返回结果集; property - java属性; column - 数据表列名; javaType - java类型 -->
<resultMap id="userMap" type="User">
<id property="id" column="id" javaType="int"></id> <!-- 主键映射 -->
<result property="username" column="username" javaType="String"></result> <!-- 普通属性映射 -->
<result property="password" column="password" javaType="String"></result>
<result property="sex" column="sex" javaType="String"></result>
<result property="address" column="address" javaType="String"></result>
</resultMap>
<!--<cache eviction="LRU" flushInterval="30000" size="1000"/>--> <!-- 缓存配置,这里不细述 -->
<!-- 定义 SQL 语句,其中 id 需要和接口中的方法名一致 -->
<!-- useGeneratedKeys:实现自动生成主键 -->
<!-- keyProperty: 唯一标记一个属性 -->
<!-- parameterType 指明查询时使用的参数类型,resultType 指明查询返回的结果集类型 -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id" parameterType="User">
insert into user (username,password,sex,address) values
(#{username},#{password},#{sex},#{address})
</insert>
<update id="updateUser" parameterType="User">
update user set
address=#{address} where
id=#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from user where
id=#{id}
</delete>
<!-- 如未为 Java Bean 起类别名,使用 resultType="com.shiyanlou.mybatis.model.User" 替代 resultMap -->
<!-- 使用resultType时,一定要保证,你属性名与字段名相同;如果不相同,就使用自定义的resultMap -->
<select id="selectUserById" parameterType="int" resultMap="userMap" >
select * from user where id=#{id}
</select>
<select id="selectAllUser" resultMap="userMap"> <!-- 不需要设置为collection,底层会自动将数据转换为列表-->
select * from user
</select>
</mapper>
测试类 - UserTest.java (位于测试文件夹下) 注意,使用 TestCase 作为测试代理,方法签名一定要以 test 开头,访问权限为 public .
import junit.framework.TestCase;
public class UserTest extends TestCase {
private static SqlSessionFactory sqlSessionFactory;
static {
// Mybatis 配置文件
String resource = "mybatis.cfg.xml";
// 得到配置文件流
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
// 创建会话工厂,传入 MyBatis 的配置文件信息
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
// 新增用戶
public void testInsertUser() {
// 通过工厂得到 SqlSession
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User();
user.setUsername("Liry");
user.setPassword("123abc");
user.setSex("female");
user.setAddress("Hongkong");
try {
mapper.insertUser(user);
session.commit();
} catch (Exception e) {
e.printStackTrace();
session.rollback();
}
// 释放资源
session.close();
}
// 更新用戶
public void testUpdateUser() {
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = null;
try {
user = mapper.selectUserById(5);
} catch (Exception e1) {
e1.printStackTrace();
}
user.setAddress("chongqing");
try {
mapper.updateUser(user);
session.commit();
} catch (Exception e) {
e.printStackTrace();
session.rollback();
}
session.close();
}
// 删除用戶
public void testDeleteUser() {
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
try {
mapper.deleteUser(3);
session.commit();
} catch (Exception e) {
e.printStackTrace();
session.rollback();
}
session.close();
}
// 根据id查询用户信息
public void testSelectUserById() {
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
try {
User user = mapper.selectUserById(null);
session.commit();
System.out.println(user.getId() + " " + user.getUsername() + " "
+ user.getPassword() + " " + user.getSex() + " "
+ user.getAddress());
} catch (Exception e) {
e.printStackTrace();
session.rollback();
}
session.close();
}
// 查询所有的用户信息
public void testSelectAllUser() {
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
try {
List<User> userList = mapper.selectAllUser();
session.commit();
for (User user : userList) {
System.out.println(user.getId() + " " + user.getUsername() + " "
+ user.getPassword() + " " + user.getSex() + " "
+ user.getAddress());
}
} catch (Exception e) {
e.printStackTrace();
session.rollback();
}
session.close();
}
************************* 要点 ***************************
● 多数据源
生成session工厂实例
InputStream inputStream = Resources.getResourceAsStream("mybatis.cfg.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
其中 build 方法,允许传入环境配置
build(InputStream inputStream)
build(InputStream inputStream, String environment)
一个 SqlSessionFactory 只对应一个数据库,则多数据源需要生成多个 SqlSessionFactory 实例。
// 配置1
InputStream inputStream1 = Resources.getResourceAsStream("mybatis1.cfg.xml");
SqlSessionFactory sqlSessionFactory1 = new SqlSessionFactoryBuilder().build(inputStream1);
// 配置2
InputStream inputStream2 = Resources.getResourceAsStream("mybatis2.cfg.xml");
SqlSessionFactory sqlSessionFactory2 = new SqlSessionFactoryBuilder().build(inputStream2);
● 有意思的自定义结果集
如果使用构造方法,可以这样自定义结果集:
<resultMap id="userMap" type="User">
<constructor>
<idArg column="id" javaType="int"/> <!-- 指出主键参数 -->
<arg column="username" javaType="String"/> <!-- 其他普通参数 -->
<arg column="password" javaType="String"/>
<arg column="sex" javaType="String"/>
<arg column="address" javaType="String"/>
</constructor>
</resultMap>
discriminator 鉴别器,选择性地返回字段 (注意,只是针对已经查询到的结果选择性返回部分字段,不会影响结果数)
<resultMap id="userMap" type="User">
<id property="id" column="id" javaType="int"/>
<result property="username" column="username" javaType="String"/>
<result property="password" column="password" javaType="String"/>
<discriminator javaType="String" column="sex"> <!-- 相当于java中的 switch -->
<case value="male" resultType="sexResult">
<!-- 符合条件则该字段被返回,否则返回空,若存在其他case,则也可能返回其他结果 -->
<result property="sex" column="sex" javaType="String"/>
</case>
</discriminator>
<result property="address" column="address" javaType="String"/>
</resultMap>
● 不支持自增长的数据插入
先查询主键,再插入
<insert id="insertUser">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select seq_users.nextval from dual
</selectKey>
insert into user (username,password,sex,address) values (#{username},#{password},#{sex},#{address})
</insert>
● 开启缓存
启用此功能可以缓存一定范围内的查询结果,单体应用可以作为备选配置,详情参考文章开头给出的文章地址。
● 一对多、多对一、多对多关系映射
在标签 resultMap 中使用 association 表示单个实体,collection 表示集合
<resultMap id="classmap" type="Classes">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<!-- 若不使用 resultMap,则写法同 collection,无需指定 column (对应实体表主键列) -->
<association property="teacher" column="c_ht_id" resultMap="teacherMap"/>
<!--<collection property="students" resultMap="studentsMap"/>-->
<collection property="students" ofType="Student"> <!-- ofType 为集合中的元素类型 -->
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
<result property="sex" column="s_sex"/>
<result property="age" column="s_age"/>
</collection>
</resultMap>
● 动态SQL
一些常用标签:
- if 动态条件
- choose 同一字段多种条件
- trim 动态增加或消除前缀或后缀
- where 智能处理and或or的增加或消除
- set 智能处理update多个字段的分隔符
- foreach 循环迭代
- bind 产生下文可引用的变量并赋值
● 注解
在java的mapper文件中使用注解,替代在xml文件写sql。(不提倡)
************************* 一些底层 ***************************
● mybatis 如何防止注入攻击?
因注入攻击只能发生在SQL编译过程,因此:
- # 使用这种符号的sql语句将会执行预编译,执行时将会使用已编译好的SQL语句,替换“?”占位符。
- $ 使用这种符号时,${}中的内容将会直接出现在编译过程中,因此不能防止注入攻击。典型的使用情形是动态替换表名或列名。