什么是Mybatis
- MyBatis 是一款优秀的持久层框架
- 它支持定制化 SQL、存储过程以及高级映射。
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
- **MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。 **
如何获取Myabtis
-
Maven仓库
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency>
-
Github
持久化
数据持久化
- 持久化就是将程序的数据在持久状态和瞬时状态转化的过程(将不能长久存放的数据持久到数据库中)
持久层
- 完成持久化工作的代码块(Dao层)
第一个Mybatis程序
思路:搭建环境–>导入Mybatis—>编写代码—>测试
-
搭建环境
-
搭建数据库
CREATE DATABASE Mybatis; use Mybatis; create table user( id int not null PRIMARY key, `NAME` varchar(30) , pwd varchar(30) )ENGINE=innodb default charset=utf8; insert into user values(1,'张三','123456'),(2,'李四','123456'),(3,'王五','123456');
-
新建项目
-
新建一个普通的Maven项目
-
删除Src文件
-
导入Maven依赖
-
<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
-
-
-
创建一个模块(普通的Maven项目 之前普通Maven项目的子类)
-
编写Mybatis工具类
package com.dyz.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.IOException; import java.io.InputStream; //sqlSessionFactory--->sqlSession public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; //使用Mybatis第一步,获取SqlSessionFactory对象 static{ String resource = "mybatis-config.xml"; InputStream inputStream = null; try { inputStream = Resources.getResourceAsStream(resource); } catch (IOException e) { e.printStackTrace(); } sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } /* 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。 SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。 */ public static SqlSession getSqlSession(){ // SqlSession sqlSession = sqlSessionFactory.openSession(); // return sqlSession; return sqlSessionFactory.openSession(); } }
-
编写Mybatis核心配置文件
<?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 核心配置文件--> <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=false&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/dyz/dao/UserMapper.xml"/> </mappers> </configuration>
-
-
编写代码
-
实体类
package com.dyz.pojo; public class User { private int id; private String name; private String pwd; public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } public User() { } 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 + '\'' + '}'; } }
-
Dao接口
package com.dyz.dao; import com.dyz.pojo.User; import java.util.List; public interface UserDao { List<User> getUserList(); }
-
接口实现类由原来的UserDaoImpl转变为Mapper配置文件;
<?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接口--> <mapper namespace="com.dyz.dao.UserDao"> <!--id就相当于原来重写接口方法的java类 有了框架以后该类可以省略--> <!--resultType是Sql返回的类型,必须精确到该类型的位置--> <select id="getUserList" resultType="com.dyz.pojo.User"> select *from user ; </select> </mapper>
-
Mapper没有在Mybatis-config.xml文件中注册
如果注册了仍然显示error may exist in…说明Mybatis-config.xml无法导出(缓存target中不存在)需要在pom.xml(父子pox.xml中都要添加并且刷新maven)中添加
<!-- 在bulid中配置resource 来防止资源导出失败的问题 并不是每个人都会遇到 遇到时可以使用-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
-
junit测试
package com.dyz.dao; import com.dyz.pojo.User; import com.dyz.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class UserDaoTest { @Test public void Test(){ //获取SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //getMapper 获取接口的实例对象mapper; UserDao mapper = sqlSession.getMapper(UserDao.class); /*执行该方法 该方法已经在UserMapper.xml中实现 而UserMapper.xml也与UserDao接口进行了绑定 因此直接mapper.方法即可执行 */ List<User> userList = mapper.getUserList(); for(User user:userList){ System.out.println(user); } sqlSession.close(); } }
CRUD
namespace
namespace绑定的包名要和Mapper/Dao接口名一致
- id:对应namespace中的方法名
- resultType:Sql语句执行的返回值!增删改都没有
- parameterType:参数列表的参数类型
-
编写接口
package com.dyz.dao; import com.dyz.pojo.User; import java.util.List; public interface UserDao { //查询全部用户 List<User> getUserList(); //根据id查用户 User getUserById( int id); //增加一个用户 int addUser(User user); //修改用户 int updateUser(User user); //删除用户 int deleteUser(int id); }
-
编写对应mapper中的sql语句
<?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绑定一个对应的Mapper接口--> <mapper namespace="com.dyz.dao.UserDao"> <!--id就相当于原来重写接口方法的java类 有了框架以后该类可以省略--> <!--resultType是Sql返回的类型,必须精确到该类型的位置--> <select id="getUserList" resultType="com.dyz.pojo.User"> select *from user </select> <!-- 根据id查用户 parameterType是方法的参数列表中参数类类型 如果是User就需要精确到具体位置--> <select id="getUserById" parameterType="int" resultType="com.dyz.pojo.User"> select * from user where id=#{id} </select> <insert id="addUser" parameterType="com.dyz.pojo.User"> insert into mybatis.user(id, NAME, pwd) VALUES (#{id},#{name},#{pwd}) </insert> <update id="updateUser" parameterType="com.dyz.pojo.User"> update mybatis.user set NAME=#{name} where id=#{id} </update> <delete id="deleteUser" parameterType="int" > delete from user where id=#{id} </delete> </mapper>
-
测试
package com.dyz.dao; import com.dyz.pojo.User; import com.dyz.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class UserDaoTest { @Test public void Test(){ //获取SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //getMapper 获取接口的实例对象mapper; UserDao mapper = sqlSession.getMapper(UserDao.class); /*执行该方法 该方法已经在UserMapper.xml中实现 而UserMapper.xml也与UserDao接口进行了绑定 因此直接mapper.方法即可执行 */ List<User> userList = mapper.getUserList(); for(User user:userList){ System.out.println(user); } sqlSession.close(); } @Test public void getUserById(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); User user= mapper.getUserById(1); System.out.println(user); sqlSession.close(); } @Test public void addUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); int result = mapper.addUser(new User(4, "章鱼", "123654789")); //增删改必须要提交事务!! sqlSession.commit(); sqlSession.close(); } @Test public void updateUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); mapper.updateUser(new User(4,"张二","147852")); //增删改必须要提交事务 sqlSession.commit(); sqlSession.close(); } @Test public void deleteUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); mapper.deleteUser(4); //增删改必须要提交事务 sqlSession.commit(); sqlSession.close(); } }
增删改需要提交事务
报错读错时从下往上读
万能的Map
当数据库中对象的字段过多时,我们可以考虑去使用Map (字段过多,但我们只需要对少数字段进行修改)
//修改用户(修改部分字段)
int updateUser2(Map<String,Object> map);
<!-- 修改用户信息2-->
<update id="updateUser2" parameterType="Map">
update mybatis.user set pwd=#{password} where id=#{id}
</update>
@Test
public void updateUser2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
HashMap<String, Object> map = new HashMap<>();
map.put("password","360669145");
map.put("id",1);
mapper.updateUser2(map);
sqlSession.commit();
sqlSession.close();
}
Map传递参数,直接在sql中去取key即可[parameterType=Map]
模糊查询
//模糊查询
List<User> getUser(String name);
<!-- 模糊查询1-->
<select id="getUser" resultType="com.dyz.pojo.User">
select *from mybatis.user where NAME like #{name}
</select>
@Test
public void getUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> user = mapper.getUser("王%");
for (User user1 : user) {
System.out.println(user1);
}
sqlSession.close();
}
配置解析
核心配置文件 Mybatis-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=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/dyz/dao/UserMapper.xml"/>
</mappers>
</configuration>
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
环境配置
Mybaits可以配置成适应多种环境
不过需要记住尽管可以配置多个环境,但每个SqlSessionFactory实例只可以选择一种环境.
Mybatis,默认的事务管理器是JDBC, 连接池 POOLED
属性
**属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。[db.properties] **
编写一个配置文件 在Mybatis核心配置文件中导入
db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
<?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>
<!-- 映入外部配置文件 自闭和标签 properties标签需要写在第一个 直接在resource文件夹下,所以不需要完整路径-->
<!-- 也可以在properties标签中引入一部分配置属性 优先使用外部配置文件中的属性-->
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 不在resource文件夹下,所以需要完整路径-->
<mapper resource="com/dyz/dao/UserMapper.xml"/>
</mappers>
</configuration>
类型别名(typeAlias)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写
typeAlias标签只能放在第三个,前面没有的话就默认为第三个
<!-- 类型别名 typeAlias-->
<typeAliases>
<typeAlias type="com.dyz.pojo.User" alias="User"/>
</typeAliases>
<select id="getUserList" resultType="User">
select *from user
</select>
2.指定一个包名,MybatisUI在包下面搜索需要的Java Bean,比如扫描实体类的包,它的默认就为这个类的类名(首字母小写)
<typeAliases>
<package name="com.dyz.pojo"/>
</typeAliases>
<select id="getUserList" resultType="user">
select *from user
</select>
第一种在实体类比较少的时候使用第一种
如果实体类十分多,建议使用第二种
但是第一种可以自定义别命,第二种不行,如果非要改可以在实体类上加注解.
@Alias("hello")
public class User {
private int id;
private String name;
private String pwd;
别命
别名 | 映射的类型 |
---|---|
_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 |
映射器
MapperRegistry:注册绑定mapper文件
方式一:推荐使用
<mappers>
<mapper resource="com/dyz/dao/UserMapper.xml"/>
</mappers>
方式二
<mappers>
<!-- 不在resource文件夹下,所以需要完整路径-->
<!-- <mapper resource="com/dyz/dao/UserMapper.xml"/>-->
<mapper class="com.dyz.dao.UserDao"/>
</mappers>
接口和mapper文件必须同名
两个文件必须在同一个包下面
方式三
<mappers>
<!-- 不在resource文件夹下,所以需要完整路径-->
<!-- <mapper resource="com/dyz/dao/UserMapper.xml"/>-->
<!-- <mapper class="com.dyz.dao.UserDao"/>-->
<package name="com.dyz.dao"/>
</mappers>
接口和mapper文件必须同名
两个文件必须在同一个包下面
作用域和生命周期
**不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。 **
SqlSessionFactoryBulider
- 一旦创建了SqlSessionFactory,就不在需要它了
- 局部变量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vcvXVuc0-1659085170242)(D:\桌面\javaPicture\1650515448008.png)]
SqlSessionFactory
- 可以理解为数据库连接池
- SqlSessionFactory一旦被创建就会在应用运行期间一直存在,没有任何理由丢弃它或者重新创建另一个实例
- 因此SqlSessionFactory的最佳作用域就是应用作用域.
- 最简单的就是使用单例模式或者静态单例模式
SqlSession
- 连接到连接池的一个请求!
- SqlSesion的实例不是线程安全的,因此是不能被共享的,所以它的最佳作用域是请求或者方法的作用域
- 用完之后需要赶紧关闭,否则资源被占用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oO9ozHRA-1659085170242)(D:\桌面\javaPicture\1650515596446.png)]
这里的每一个Mapper就代表一个具体的业务
解决属性名和字段名不一致的问题
数据库中的字段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5I1HN4DO-1659085170243)(D:\桌面\javaPicture\1650517663397.png)]
测试实体类字段不一致的情况
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sqjk1z21-1659085170244)(D:\桌面\javaPicture\1650517885165.png)]
测试结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CCc3DNPP-1659085170245)(D:\桌面\javaPicture\1650518074556.png)]
//select *from user where id=1
//找不到pwd 因此password为null
//select id,name,pwd from user where id=1
解决办法
-
起别名
<select id="getUserById" parameterType="int" resultType="com.dyz.pojo.User"> select id,name,pwd as password from mybatis.user where id=#{id} </select>
-
结果集映射
id name pwd id name password
<!-- id是该标签的唯一属性 与select标签中resultMap相对应 type是实体类的名称 我们在配置文件中起了别名此处可以直接用别名--> <resultMap id="UserMap" type="User"> <result column="pwd" property="password"/> </resultMap> <select id="getUserById" resultMap="UserMap"> select * from mybatis.user where id=#{id} </select>
日志
日志工厂
如果一个数据库操作出现了异常,我们需要排错,日志就是我们最好的助手.
曾经:sout debug
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u8X4kigM-1659085170246)(D:\桌面\javaPicture\1650602228720.png)]
- SLF4J
- LOG4J(deprecated since 3.5.9) (掌握)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING (掌握)
- NO_LOGGING
在Mybatis中具体使用哪一个日志实现,在设置中设定!
STDOUT_LOGGING
STDOUT_LOGGING标准日志输出
<!--properties下面-->
<!-- 日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MMW9mIMD-1659085170247)(D:\桌面\javaPicture\1650602863697.png)]
LOG4J
什么是Log4j
-
我们也可以控制每一条日志的输出格式
-
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
-
可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
-
先导入log4j的依赖
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
LOG4J.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=【%c】-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/kuang.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=【%p】【%d{yy-MM-dd}】【%c】%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
-
配置log4j为日志的实现
<!-- log4j日志实现-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
-
LOG4J的使用,直接测试运行刚才的查询
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y5wAM2Hf-1659085170248)(D:\桌面\javaPicture\1650604958597.png)]
简单使用
1.在要使用Log4j的类中导入包 import org.apache.log4j.Logger;
2.日志对象,参数为当前类的class
static Logger logger= Logger.getLogger(UserMapperTest.class);
-
日志级别
log4j的日志信息一共分为五个级别:DEBUG、INFO、WARN、ERROR和FATAL。这五个级别是有顺序的,DEBUG < INFO < WARN < ERROR < FATAL,明白这一点很重要,这里Log4j有一个规则:假设设置了级别为P,如果发生了一个级别Q比P高,则可以启动,否则屏蔽掉。
- DEBUG: 这个级别最低的东东,一般的来说,在系统实际运行过程中,一般都是不输出的。因此这个级别的信息,可以随意的使用,任何觉得有利于在调试时更详细的了解系统运行状态的东东,比如变量的值等等,都输出来看看也无妨。
- INFO:这个应该用来反馈系统的当前状态给最终用户的,所以,在这里输出的信息,应该对最终用户具有实际意义,也就是最终用户要能够看得明白是什么意思才行。从某种角度上说,Info 输出的信息可以看作是软件产品的一部分(就像那些交互界面上的文字一样),所以需要谨慎对待,不可随便。
- WARN、ERROR和FATAL:警告、错误、严重错误,这三者应该都在系统运行时检测到了一个不正常的状态,他们之间的区别,要区分还真不是那么简单的事情。我大致是这样区分的:所谓警告,应该是这个时候进行一些修复性的工作,应该还可以把系统恢复到正常状态中来,系统应该可以继续运行下去。
所谓错误,就是说可以进行一些修复性的工作,但无法确定系统会正常的工作下去,系统在以后的某个阶段,很可能会因为当前的这个问题,导致一个无法修复的错误(例如宕机),但也可能一直工作到停止也不出现严重问题。
所谓Fatal,那就是相当严重的了,可以肯定这种错误已经无法修复,并且如果系统继续运行下去的话,可以肯定必然会越来越乱。这时候采取的最好的措施不是试图将系统状态恢复到正常,而是尽可能地保留系统有效数据并停止运行。
也就是说,选择 Warn、Error、Fatal 中的具体哪一个,是根据当前的这个问题对以后可能产生的影响而定的,如果对以后基本没什么影响,则警告之,如果肯定是以后要出严重问题的了,则Fatal之,拿不准会怎么样,则 Error 之。
分页
使用Limit分页
#从下标1(第二个数据)开始查两行数据 select *from mybatis.user limit 1,2 # 从下标0开始查询两行数据 相当于limit 0,2 select *from mybatis.user limit 2;
使用MyBatis实现分页
-
接口
//分页 List<User> getUserByLimit(Map<String,Integer> map);
-
Mapper.xml
<!-- 分页查询--> <select id="getUserByLimit" parameterType="map" resultMap="UserMap"> select *from mybatis.user LIMIT #{startIndex},#{pageSize} </select>
-
Test
@Test public void getUserByLimitTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Integer> map = new HashMap<>(); map.put("startIndex",1); map.put("pageSize",3); List<User> userByLimit = mapper.getUserByLimit(map); for (User user : userByLimit) { System.out.println(user); } sqlSession.close(); }
-
RowBounds分页查询(扩展了解)
接口
//分页2 List<User> getUserByRowBounds();
mapper
<!-- 分页查询2 熟悉 不重要--> <select id="getUserByRowBounds" resultMap="UserMap"> select *from mybatis.user </select>
Test
@Test public void getUserByLimitTest2(){ RowBounds rowBounds = new RowBounds(1, 2); SqlSession sqlSession = MybatisUtils.getSqlSession(); List<User> users = sqlSession.selectList("com.dyz.dao.UserMapper.getUserByRowBounds", null, rowBounds); for (User user : users) { System.out.println(user); } sqlSession.close(); }
分页插件
使用注解开发
- 接口
//查询全部用户
@Select("select *from user")
List<User> getUserList();
-
核心配置文件中绑定接口
<mappers> <!-- 不在resource文件夹下,所以需要完整路径--> <!-- <mapper resource="com/dyz/dao/UserMapper.xml"/>--> <mapper class="com.dyz.dao.UserMapper"/> <!-- <package name="com.dyz.dao"/>--> </mappers>
-
Test
@Test public void getUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUserList(); for (User user : userList) { System.out.println(user); } sqlSession.close(); }
CRUD
-
在工具类创建(返回sqlSession)的时候实现自动提交事务
public static SqlSession getSqlSession(){ // SqlSession sqlSession = sqlSessionFactory.openSession(); // return sqlSession; return sqlSessionFactory.openSession(true); }
-
编写接口,增加注解
package com.dyz.dao; import com.dyz.pojo.User; import org.apache.ibatis.annotations.*; import java.util.List; import java.util.Map; public interface UserMapper { //查询全部用户 @Select("select *from user") List<User> getUserList(); //根据id查询用户 @Select("select *from user where id=#{id} and name=#{name} ") User getUserByID(@Param("id") int id,@Param("name") String name); //增加用户 //#{参数和实体类中相同} @Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})") int addUser(User user); //修改用户 @Update("update user set name=#{name},pwd=#{password} where id=#{id}") int updateUser(@Param("name") String name,@Param("password") String password,@Param("id") int id); //删除用户 @Delete("delete from user where id=#{id}") int deleteUser(@Param("id") int id); }
package com.dyz.dao; import com.dyz.pojo.User; import org.apache.ibatis.annotations.Param; public interface UserMapper { //查询User User getUserById(@Param("id") int id,@Param("name") String name); }
<mapper namespace="com.dyz.dao.UserMapper"> <select id="getUserById" resultType="user"> select *from mybatis.user where id=#{id} and name=#{name} #{使用注解括号中的参数} </select> </mapper>
public class UserMapperTest { @Test public void getUserByIdTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1, "张三"); System.out.println(user); sqlSession.close(); } }
关于@Param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议大家都加上
- 我们在SQL中的引用就是我们这里的@Param()中设定的属性名
Mybatis执行流程
-
Resource.getResourceAsStream:获取Mybatis-config.xml配置文件的数据流
-
build调用XMLConfigBuilder(inputStream, environment, properties);解析配置文件,解析完之后传给configuration
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { SqlSessionFactory var5; try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); var5 = this.build(parser.parse()); } catch (Exception var14) { throw ExceptionFactory.wrapException("Error building SqlSession.", var14); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException var13) { } } return var5; }
-
SqlSession中创建transaction事务管理器
-
SqlSession中创建executor执行器
-
创建SqlSession实例
-
实现CRUD
-
查看是否执行成功
-
提交事务
-
关闭
-
Lombok
使用步骤
-
在IDEA中安装Lombok插件
-
在项目中导入lombok的jar包
-
添加注解
@Getter and @Setter @FieldNameConstants @ToString @EqualsAndHashCode @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data @Builder @SuperBuilder @Singular @Delegate @Value @Accessors @Wither @With @SneakyThrows @val @var experimental @var @UtilityClass
@Data
public class User {
int id;
String name;
String password;
}
@Data:无参构造,get set toString hashcode,equals
有参无参
@AllArgsConstructor
@NoArgsConstructor
放在类上所有属性都有 放在单个属性上 单个属性有
@toString
@Getter
多对一处理
例:多个学生有一个老师 对学生而言就是多对一
测试环境搭建
-
导入lombok依赖 如果父类文件中有 就不需要
-
新建实体类Teacher Student
-
建立Mapper接口
-
家里Mapper.xml文件
-
在核心配置文件中绑定注册我们的Mapper接口或者文件
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NIAwPi4R-1659085170249)(D:\桌面\javaPicture\1650949308237.png)]
即使Mapper.xml文件与Mapper接口不在同一个大的资源文件下,但是其路径名相同,编译后也会在同一个文件下.
按照查询嵌套处理
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dyz.dao.StudentMapper">
<select id="getStudentTeacher" resultMap="studentTeacher">
select *from mybatis.student;
</select>
<!--思路:先查询出所有学生信息
根据查询出学生信息中的tid 寻找对应的老师
-->
<resultMap id="studentTeacher" type="Student">
<!-- javaType 是根据tid查询到的对象类型 select是查询该对象执行的方法-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select *from mybatis.teacher where id=#{tid} 此处#{tid}系统会自动匹配 {}里面可以随意写但不建议
</select>
</mapper>
按照结果嵌套处理
<select id="getStudentTeacher2" resultMap="studentTeacher2">
select s.id sid,s.name sname,t.name tname from
mybatis.student s ,mybatis.teacher t where s.tid=t.id
</select>
<resultMap id="studentTeacher2" type="student" >
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<!-- 上面查询语句涉及到Teacher表中的name 一次你 此处需要给name添加结果集映射-->
<result property="name" column="tname"/>
</association>
</resultMap>
原因是resultMap属性指向不正确
报错: Result Maps collection does not contain value for com.dyz.dao.StudentMapper.
一对多处理
例:一个老师有多个学生 对老师而言就是一对多
-
按照结果嵌套处理
<select id="getTeacher" resultType="Teacher"> select *from mybatis.teacher </select> <select id="getTeacherStudent" resultMap="TeacherStudent"> select s.id sid,s.name sname,t.id tid,t.name tname from mybatis.teacher t,mybatis.student s where s.tid=t.id and t.id=#{id} </select> <resultMap id="TeacherStudent" type="Teacher"> <result column="tid" property="id"/> <result column="tname" property="name"/> <!-- 复杂的属性,我们需要单独处理,对象: association 集合:collection javaType=""指定属性的类型 ofType 集合中的泛型信息--> <collection property="students" ofType="Student"> <result column="sid" property="id"/> <result column="sname" property="name"/> <result column="tid" property="tid"/> </collection> </resultMap>
-
按照查询嵌套处理
<select id="getTeacherStudent2" resultMap="teacherStudent2"> select *from mybatis.teacher where id=#{tid} </select> <resultMap id="teacherStudent2" type="Teacher"> <collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/> </resultMap> <select id="getStudentByTeacherId" resultType="student"> select *from mybatis.student where tid=#{id} </select>
小结
-
关联-association[多对一]
-
集合-collection [一对多]
-
javaType ofType
javaType:用来指定实体类中属性的类型
ofType:用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
动态sql
什么是动态SQL:动态SQL就是指根据不同的条件生成不同的SQL语句
-
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。 if choose (when, otherwise) trim (where, set) foreach
搭建环境
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8
创建一个基础工程
- 导包
- 编写配置文件
- 编写实体类对应的Mapper接口和Mapper.XML文件
IF
//根据要求查数据
List<Blog> getBlogByIf(Map map);
<select id="getBlogByIf" parameterType="map" resultType="Blog">
# 如果不传author 和 title name满足1=1的数据都会被查询出来
select * from mybatis.blog where 1=1 (正常不会出现1=1 会是其他条件)
<if test="author!=null">
and author=#{author}
</if>
<if test="title!=null">
and title=#{title}
</if>
</select>
@Test
public void getBlogByIf(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("author","狂神说"); // //如果什么都不传,会查出所有结果
List<Blog> blogByIf = mapper.getBlogByIf(map);
for (Blog blog : blogByIf) {
System.out.println(blog);
}
sqlSession.close();
}
choose、when、otherwise
//根据要求查数据2
List<Blog> getBlogByIf2(Map map);
</select>
<select id="getBlogByIf2" parameterType="map" resultType="Blog">
select *from mybatis.blog //choose 相当于 switch when相当于case 满足条件就跳出 因此查询结果根据第一个满足的条件来决定
<where>
<choose>
<when test="title!=null">
and title=#{title}
</when>
<when test="views!=null">
views=#{views}
</when>
</choose>
</where>
</select>
@Test
public void getBlogByIf2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Mybatis"); //如果什么都不传,会查出所有结果
map.put("views",10000);
List<Blog> blogByIf = mapper.getBlogByIf(map);
for (Blog blog : blogByIf) {
System.out.println(blog);
}
sqlSession.close();
}
trim(where set)
where
<select id="getBlogByIf" parameterType="map" resultType="Blog">
select * from mybatis.blog where 1=1 //此处如果没有条件且下面第一个author条件不满足那么语句就会执行失败
//因为查询sql语句会变成 select * from mybatis.blog where and title=#{title}
<if test="author!=null">
and author=#{author}
</if>
<if test="title!=null">
and title=#{title}
</if>
</select>
修改成如下就会自动把and去掉
<select id="getBlogByIf" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<if test="author!=null">
and author=#{author}
</if>
<if test="title!=null">
and title=#{title}
</if>
</where>
</select>
set
//更新数据
int updateBlog(Map map);
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="author!=null">
author=#{author},
</if>
<if test="views!=null">
views=#{views}
</if>
where id=#{id}
</set>
</update>
@Test
public void updateBlog(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("author","张宇说"); //修改的数据需要和sql中的对应 不能sql有 却没有数据传过去
map.put("views",998998);
mapper.updateBlog(map);
sqlSession.close();
}
所谓动态SQL,本质上还是SQL语句.只是我们可以在SQL层面去执行一个逻辑代码
SQL片段
有的时候,我们可能会将一些功能的部分抽取出来,方便复用!
1.使用SQL标签抽取公共的部分
<sql id="select-title-views">
<choose>
<when test="title!=null">
and title=#{title}
</when>
<when test="views!=null">
views=#{views}
</when>
</choose>
</sql>
-
在需要使用的地方使用include标签引用即可
<select id="getBlogByIf2" parameterType="map" resultType="Blog"> select *from mybatis.blog <where> <include refid="select-title-views"/> </where> </select>
注意事项
- 最好基于单表来定义SQL片段
- 不要存在where标签
foreach
//根据id集合查询博客
List<Blog> getBlogByIdList(Map map);
<!-- select *from Mybatis.blog where id in(1,2,3)-->
<select id="getBlogByIdList" parameterType="map" resultType="blog">
# select *from Mybatis.blog where (id=1 or id=2 or id=3)
select *from mybatis.blog
<where>
# collection是存储id的集合名 通过参数map传过来 item是集合中存放的数据名 separator 集合中数据的分隔符
<foreach collection="idl" item="id" open="(" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
@Test
public void getBlogByIdList(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
HashMap map = new HashMap();
map.put("idl",list);
List<Blog> blog = mapper.getBlogByIdList(map);
for (Blog blog1 : blog) {
System.out.println(blog1);
}
sqlSession.close();
}
动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式去排列组合就可以了
建议:
- 先在Mysql中写出完整的SQL,再对应的去修改成为我们的动态SQL的实现
缓存
简介
查询:需要连接到数据库,耗资源!
一次查询的结果,给他暂存在一个可以直接取到的地方---内存即缓存
我们可以再次查询相同数据的时候,直接走缓存,就不用走数据库了
- 什么是缓存(cache)
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题.
- 为什么使用缓存?
- 减少和数据库交互的次数,减少系统开销,提高系统效率
- 什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据
Mybatis缓存
- Mybatis包含了一个非常强大的缓存特性,他可以非常方便的定制和配置缓存,缓存可以极大地提高查询到效率
- Mybatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存
一级缓存
- 一级缓存也叫本地缓存:SqlSession
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
测试步骤
-
开启日志
-
测试在一个Session中查询两次相同的记录
-
查看日志输出
g-blog.csdnimg.cn/645f374aca724ed287e7ed8fe0a21b7a.png#pic_center)
只查询了一次
缓存失效的情况
- 查询不同的东西
2.增删改操作,可能会改变原来的数据,所以必定会刷新缓存(再次执行sql)
-
手动刷新缓存
public void getUserByIdTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); // sqlSession.clearCache(); 此语句放在执行接口方法上面就不会影响下面的语句执行 User user = mapper.getUserById(1); System.out.println(user); System.out.println("=============="); sqlSession.clearCache(); //放在此处则会影响 会重新执行sql访问数据库 User user2 = mapper.getUserById(1); System.out.println(user2); sqlSession.close(); }
小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段!这个区间段外是会刷新缓存的
一级缓存就是一个Map
二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间对应一个二级缓存
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是,会话关闭了 一级缓存中的数据被保存到二级缓存中
- 新的会话查询信息,就可以从二级缓存中获取内容
- 不同的mapper查出的数据会被放在自己对应的缓存(map)中
步骤:
-
开启全局缓存
<!-- 显示开启二级缓存--> <setting name="cacheEnabled" value="true"/>
-
在要使用二级缓存的mapper中开启
<!-- 在当前mapper.xml文件中使用二级缓存--> <cache/>
也可以自定义参数
<!-- 在当前mapper.xml文件中使用二级缓存--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
-
测试
问题:我们需要将实体类序列化(继承Serializable接口)否则会报错
java.io.NotSerializableException: com.dyz.pojo.User
小结:
-
只要开启了二级缓存,在同一个Mapper下就有效
-
所有的数据都会放在一级缓存中
-
只有当会话提交或者关闭的时候才会提交到二级缓存中
-
缓存顺序:
先看二级缓存中有没有
再看一级缓存中有没有
最后去查数据库