Mybatis
MybatisXML配置官网 https://mybatis.org/mybatis-3/zh/configuration.html#settings
三.mybatis中的增删改查
1.在父类工程的pom.xml文件中的配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>mybatis_learn</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>mybitis-01</module>
<module>mybatis-02</module>
<module>mybatis-03</module>
<module>mybatis-04</module>
</modules>
<!-- 父工程-->
<dependencies>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<!-- 在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
2.在resources中添加配置文件(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是核心配置文件-->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<!-- 每一个Mapper.XML都需要在Mybatis核心配置文件中注册!!!-->
<mappers>
<mapper resource="com/xinzhi/Dao/UserMapper.xml"></mapper>
</mappers>
</configuration>
3.编写util工具类
/**
* @author 王皓皓
* 读取配置文件
*/
public class MybatisUtil04 {
private static SqlSessionFactory sqlSessionFactory ;
static {
try {
// 使用Mybatis获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession () {
return sqlSessionFactory.openSession();
}
}
4.在pojo文件下编写实体类
/**
* 实体类
* @author 王皓皓
*/
public class User {
private int id;
private String name;
private String pwd;
public User () {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
5.在dao文件下编写接口UserMapper和其所对应的xml文件UserMapper.xml (命名必须相同)
public interface UserMapper {
/**
* 查询全部用户
* @return 用户集合
*/
List<User> getUserList();
/**
* 通过id查询用户信息
* @param id 用户id
* @return 用户对象
*/
User getUserById (int id);
/**
* 万能Map
* @param map
* @return
*/
User getUserById2 (Map <String,Object> map);
/**
* 添加一个新用户
* @param user 用户信息
* @return 受影响行数
*/
int addUser (User user);
/**
* 万能map
* @param map
* @return
*/
int addUser2 (Map<String,Object> map);
/**
* 修改用户
* @param user 用户信息
* @return 受影响行数
*/
int changeUser (User user);
/**
* 删除用户
* @param id 用户id
* @return 受影响行数
*/
int deleteUser (int id);
/**
* 模糊查询
* @param value
* @return
*/
List<User> userList (String value);
}
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.xinzhi.Dao.UserMapper">
<!-- sql查询语句-->
<!-- 查询全部用户-->
<select id="getUserList" resultType="com.xinzhi.Pojo.User">
select id,name,pwd from users
</select>
<!-- 查询用户id-->
<select id="getUserById" parameterType="int" resultType="com.xinzhi.Pojo.User">
select id,name,pwd from mybatis.users where id = #{id}
</select>
<!-- 添加新用户-->
<insert id="addUser" parameterType="com.xinzhi.Pojo.User" >
insert into mybatis.users values (#{id}, #{name}, #{pwd} )
</insert>
<!-- 万能Map,对象中的属性可以直接取出来 传递map的key -->
<insert id="addUser2" parameterType="map" >
insert into mybatis.users(id,name,pwd) values (#{userId}, #{userName}, #{userPwd} )
</insert>
<!-- 万能Map-->
<select id="getUserById2" parameterType="map" resultType="com.xinzhi.Pojo.User">
select id,name,pwd from mybatis.users where id = #{userId} and name = #{userName}
</select>
<!-- 修改一个用户-->
<update id="changeUser" parameterType="com.xinzhi.Pojo.User" >
update mybatis.users set name=#{name}, pwd=#{pwd} where id = #{id}
</update>
<!-- 删除用户-->
<delete id="deleteUser" parameterType="int">
delete from mybatis.users where id = #{id}
</delete>
<!-- 模糊查询-->
<select id="userList" resultType="com.xinzhi.Pojo.User" >
select * from users where name like #{value}
</select>
</mapper>
6.mybatis的util工具类
private static SqlSessionFactory sqlSessionFactory ;
static {
try {
// 使用Mybatis获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 既然有了SqlSessionFactory,我们就可以从中获取SqlSession的实例
* SqlSession 完全包含了面向数据库执行SQL 命令所需的所有方法
* @return
*/
public static SqlSession getSqlSession () {
return sqlSessionFactory.openSession();
}
7.万能Map
假设,实体类,或者数据库表,字段或者参数过多,应该考虑用Map(没有叫万能map这一说,但是比较偏门实用)
接口参数传Map
/**
* 万能map
* @param map
* @return
*/
int addUser2 (Map<String,Object> map);
<!-- 万能Map,对象中的属性可以直接取出来 传递map的key -->
<insert id="addUser2" parameterType="map" >
insert into mybatis.users(id,name,pwd) values (#{userId}, #{userName}, #{userPwd} )
</insert>
@Test
public void addUser2 () {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("userId",5);
map.put("userName","打酱油");
map.put("userPwd","456654");
userMapper.addUser2(map);
// 提交事物
sqlSession.commit();
sqlSession.close();
}
/**
* 万能Map
* @param map
* @return
*/
User getUserById2 (Map <String,Object> map);
-----------------------------------------------------------------------------------
<select id="getUserById2" parameterType="map" resultType="com.xinzhi.Pojo.User">
select id,name,pwd from mybatis.users where id = #{userId} and name = #{userName}
</select>
--------------------------------------------------------------------------------
@Test
public void getUserById2 () {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("userId",2);
map.put("userName","小李");
User u = userMapper.getUserById2(map);
System.out.println( u.toString());
sqlSession.commit();
sqlSession.close();
}
模糊查询怎么写? (一般用不到,用在业务里)
代码执行的时候使用通配符%%
在sql中拼接使用通配符
select * from users where name like #{value} 传值 “%李%” 不能这么写容易sql注入
select * from users where name like %#{value}% 传值 " 李 " 不会造成sql注入
/**
* 模糊查询
* @param value
* @return
*/
List<User> userList (String value);
-------------------------------------------------------------------------------------
<select id="userList" resultType="com.xinzhi.Pojo.User" >
select * from users where name like #{value}
</select>
------------------------------------------------------------------------------
// 模糊查询
@Test
public void userLike () {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> user = userMapper.userList("%李%");
for (User user1 : user) {
System.out.println(user1.toString());
}
sqlSession.commit();
sqlSession.close();
}
四.配置解析
1 核心配置文件
mybatis-config.xml
MyBatis 的配置文件包含了会深深影响MyBatis行为的设置和属性信息
configuration(配置)
-
properties(属性)
-
settings(设置)
-
typeAliases(类型别名)
-
typeHandlers(类型处理器)
-
objectFactory(对象工厂)
-
plugins(插件)
-
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
-
databaseIdProvider(数据库厂商标识)
-
mappers(映射器)
2.环境配置(environments)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中,现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。 所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单: 每个数据库对应一个 SqlSessionFactory 实例
Mybatis默认的事物管理器就是jdbc,连接池POOLED
3.属性(properties)
我们可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
编写一个配置文件(db.properties)个人写在了resources
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
userName=root
userWord=root
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sx7LHZZF-1620185761288)(C:\Users\王皓皓\Desktop\Mybatis.assets\1620035420078.png)]
以上报错原因是 在xml中,所有的标签都可以规定其顺序
必须按照上面提示的顺序来写
在核心配置文件中引入
<properties resource="db.properties">
<property name="userName" value="root"/>
<property name="pwd" value="root"/>
</properties>
第一种引入方式(外部引入)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-srfHUNF0-1620185761291)(C:\Users\王皓皓\Desktop\Mybatis.assets\1620036175764.png)]
第二种引入方式(内部引入加外部 注:#在properties是注释)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ujGrZdFE-1620185761291)(C:\Users\王皓皓\Desktop\Mybatis.assets\1620036305700.png)]
- 可以直接引入外部文件
- 可以在其中增加一些属性配置
- 如果两个文件有同一个字段,优先使用外部配置文件!
4.类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。
它仅用于 XML 配置,意在降低冗余的全限定类名书写
<!-- 给实体类起别名-->
<typeAliases>
<typeAlias type="com.xinzhi.pojo.User" alias="User"/>
</typeAliases>
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
扫描实体类的包,它默认别名就为这个类的类名,首字母小写
<!-- 给实体类起别名-->
<typeAliases>
<package name="com.xinzhi.pojo" />
</typeAliases>
在实体类比较少的时候,用第一种方式
如果实体类非常多,建议第二种
第一张可以DIY(自定义)别名,第二种不行,如果非要改的话,需要在实体类上增加注解
package com.xinzhi.pojo;
import org.apache.ibatis.type.Alias;
/**
* 实体类
* @author 王皓皓
*/
@Alias("niHao")
public class 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 |
5.设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true | false | false |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
---|---|---|---|
6.其他
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
7.映射器(mappers)
MapperRegistry:绑定注册我们的Mapper文件
第一种:使用相对于类路径(推荐使用,用的多)
<!-- 每一个Mapper.XML都需要在Mybatis核心配置文件中注册!!!-->
<mappers>
<mapper resource="com/xinzhi/Dao/UserMapper.xml"></mapper>
</mappers>
第二种:使用class文件绑定注册
<mappers>
<mapper class="com.xinzhi.dao.UserMapper"></mapper>
</mappers>
使用class注册的注意点:
接口和它的Mapper配置文件必须同名
接口和它的Mapper配置文件必须在同一个包下
方式三:使用扫描包进行注入绑定
<mappers>
<!-- <mapper resource="com/xinzhi/Dao/UserMapper.xml"></mapper>-->
<!-- <mapper class="com.xinzhi.dao.UserMapper"></mapper>-->
<package name="com.xinzhi.dao"/>
</mappers>
使用扫描包注册的注意点:
接口和它的Mapper配置文件必须同名
接口和它的Mapper配置文件必须在同一个包下
会出现的报错信息未注册Mapper
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sJ8NAhWx-1620185761292)(C:\Users\王皓皓\Desktop\Mybatis.assets\屏幕截图 2021-05-03 192720.png)]
8.作用域(Scope)和生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKligo76-1620185761294)(C:\Users\王皓皓\Desktop\Mybatis.assets\1620091342636.png)]
作用域和生命周期是至关重要的,因为错误的使用会导致非常严重的并发问题
SqlSessionFactoryBuilder:
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了
最佳作用域是方法作用域(也就是局部方法变量)
SqlSessionFactory:
说白了可以理解为数据库连接池
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
SqlSessionFactory 的最佳作用域是应用作用域
最简单的就是使用单例模式或者静态单例模式(保证全局只有一个变量)
SqlSession:
连接到连接池的一个请求
每个线程都应该有它自己的 SqlSession 实例。SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
用完后要赶紧关闭,否则会造成资源的占用
五.解决属性名和字段名不一致的问题
1.问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jOGBhI6D-1620185761296)(C:\Users\王皓皓\Desktop\Mybatis.assets\Mybatis.assets\1620094468507.png)]数据库中的字段
新建项目,拷贝之前的,测试实体类字段不一致的情况(原来是pwd改为passWord)
public class User {
private int id;
private String name;
private String passWord;
测试出现问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9TN1s81I-1620185761297)(C:\Users\王皓皓\Desktop\Mybatis.assets\Mybatis.assets\1620094496779.png)]
解决方法:
起别名(没什么卵用)
<!-- 查询用户id-->
<select id="getUserById" parameterType="_int" resultType="com.xinzhi.pojo.User">
select id,name,pwd as passWord from mybatis.users where id = #{id}
</select>
2.resultMap
结果集映射
<!-- sql查询语句-->
<!-- 查询用户id-->
<!-- 结果集映射-->
c
<select id="getUserById" resultMap="userMap">
select id,name,pwd from mybatis.users where id = #{id}
</select>
- resultMap元素是Mybatis中最重要最强大的元素
- ResultMap的设计思想,对于简单的语句根本不需要配置显示的结果映射,而对于一些复杂一点的语句只需描述它们的关系即可
- ResultMap最优秀的地方在于,虽然你已经对他相当了解了,但是根本不需要显示地用到他们
- 如果世界总是这么简单就好了
六.日志
6.1日志工厂
如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的助手
以前用:sout,debug
现在用:日志工厂
SLF4J
LOG4J (掌握)
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING (掌握)
NO_LOGGING
其他了解
STDOUT_LOGGING标准日志输出
在mybatis配置核心文件配置我们的日志
控制台输出以下信息
Opening JDBC Connection
Created connection 1291113768.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528]
> Preparing: select id,name,pwd from mybatis.users where id = ?
> Parameters: 2(Integer)
< Columns: id, name, pwd
< Row: 2, 小李, 111
<== Total: 1
User{id=2, name=‘小李’, passWord=‘null’}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528]
Returned connection 1291113768 to pool.
Process finished with exit code 0
6.2 Log4j
百度查用法
七.分页
7.1使用Limit分页
SELECT * from user limit startIndex,pagesize
使用mybatis实现分页,核心sql
1.接口
/**
* 分页
* @param map
* @return
*/
List<User> getUserByLimit(Map<String, Integer> map);
2.Mapper.xml
<resultMap id="userMap" type="u">
<!-- column数据库中的字段,property实体类中的属性 -->
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="passWord"/>
</resultMap>
<select id="getUserByLimit" parameterType="map" resultMap="userMap">
select * from users limit #{startIndex},#{pageSize}
</select>
3.测试
@Test
public void getUserByLimit () {
SqlSession sqlSession = MybatisUtil04.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",3);
List<User> userList = userMapper.getUserByLimit(map);
for (User uuu: userList) {
System.out.println(uuu);
}
sqlSession.close();
}
7.2 RowBounds分页(简单了解)
百度查就行一大堆
八.使用注解开发
8.1面向接口编程
–根本原因:解耦,可扩展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好
关于接口的理解。
–接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
接口的本身反映了系统设计人员对系统的抽象理解。
接口应有两类:第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
一个体有可能有多个抽象面。
抽象体与抽象面是有区别的。
三个面向区别
面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法
面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题
8.2使用注解开发
1.注解在接口上实现
/**
* 查询全部用户
* @return
*/
@Select("select * from users")
List<User> getUsers ( );
2.需要再核心配置文件中绑定接口(coonfig.xml)
<!-- 绑定接口-->
<mappers>
<mapper class="com.xinzhi.dao.UserMapper"/>
</mappers>
3.测试
@Test
public void test () {
SqlSession sqlSession = MybatisUtil05.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUsers();
for (User uuu: userList
) {
System.out.println(uuu);
}
sqlSession.close();
}
本质:反射机制实现
底层:动态代理!
增删改查仿造上面来写就ok