·MyBatis是一款优秀的持久层框架
·它支持定制化SQL、存储过程和高级映射
·MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
·可以使用简单的XML或注解来配置和映射原生类型、接口和JAVA的POJO(Plain Old Java Objecrs,普通老式Java对象)为数据库中的记录
·MyBatis本来是apache的开源项目iBatis,2010年迁移到goole code并改名,2013年迁移到Github
*1.1如何获取MyBatis?*
·maven仓库(3.4.6版本)
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
·Github
·中文网:MyBatis中文网
*1.2什么是持久层*
*数据持久化*
·持久化就是将程序的数据在持久状态和瞬时状态转化的过程
·内存:*断电即失*
·数据库(JDBC),io文件 这两种方式能让数据持久化。
·持久化概念:举生活的例子,如冷藏,使食物持久化
*为什么需要持久化?*
·因为内存会断电即失,有一些对象不能让他丢失
·内存太贵了
*1.2.1持久层*
Dao层、Service层(业务操作)、Controller层(接受用户请求并且把用户的请求转化给业务去做)....
·完成持久化工作的代码块
·层的界限十分明显。
*1.3为什么需要MyBatis?*
·帮助程序员将数据存入到数据库中
·方便
·传统的JDBC代码复杂
·是一个简化的,自动化的框架
·学习框架更容易上手
Spring SpringMVC SpringBoot
2. *第一个MyBatis程序*
思路:搭建环境——>导入MyBatis*——>*编写代码——>测试
*2.1搭建环境*
(1) 搭建数据库
(2) 新建项目
1. 新建一个普通的maven项目
2. 删除src目录
3. 导入maven依赖
<!--导入依赖--> <dependencies> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.21</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
(3)创建一个模块
·编写mybatis的核心配置文件
<!--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&uxdunicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="753123"/> </dataSource> </environment> </environments>
·编写mybatis工具类
//sqlSessionFactory工厂模式 构建session public class MybatisUtils { //提升作用域 private static SqlSessionFactory **sqlSessionFactory**; static{ try { //使用Mybatis第一步:获取sqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.**getResourceAsStream**(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。// // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。 public static SqlSession getSqlSession(){ return **sqlSessionFactory**.openSession(); } }
(4)编写代码
·实体类
//实体类 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 + '\'' + '}'; } }
·Dao接口
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=绑定一个对应的Dao/Mapper接口--> <mapper namespace="com.mybatis.dao.UserDao"> <!-- 查询语句--> <!-- 标签id相当于重写方法的名字,标签内写执行的sql语句--> <select id="getUserList" resultType="com.mybatis.pojo.User" > select * from mybatis.user; </select> </mapper>
(5)测试
注意点:MapperRegistry是什么?
核心配置文件中注册mappers
·junit测试
@Test public void test(){ //第一步:获得SqlSession对象 SqlSession sqlSession = MybatisUtils.**getSqlSession**(); //执行SQL //方式一:getMapper UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList= mapper.getUserList(); for (User user : userList) { System.**out**.println(user); } //关闭SqlSession sqlSession.close(); }
测试中可能会遇到的问题:
\1. 配置文件没有注册
\2. 绑定接口错误
\3. 方法名不对
\4. 返回类型不对
\5. Maven到处资源问题
*核对接口:*
*SqlSessionFactoryBuilder*
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
*SqlSessionFactory*
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
*SqlSession*
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
*3.CRUD*
1.namespace(命名空间)
namespace中的包名要和Dao/mapper接口的包名一致
2.Select
选择,查询语句:
·id::就是对应的namespace中的方法名;
·result Type:SQL语句执行的返回值(Class,int...)
·parameterType:参数类型;
(1)编写接口
//根据id查询用户 User getUserById(int id);
(2)编写对应的mapper中的sql语句
<select id="getUserById" parameterType="int" resultType="com.mybatis.pojo.User"> select * from mybatis.user where id = #{0}; </select>
(3)测试
@Test public void testUserById(){ SqlSession sqlSession = MybatisUtils.**getSqlSession**(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.**out**.println(user); sqlSession.close(); }
\4. Insert、Update、Delete
注意点:增删改需要提交事务
(1)接口
(2)sql语句
(3)测试
//增删改需要提交事务 @Test public void addUser(){ SqlSession sqlSession = MybatisUtils.**getSqlSession**(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int res= mapper.addUser(new User(4,"嘎嘎","12580")); if (res>0){ System.**out**.println("插入成功"); } //提交事务,不提交事务数据库不反应 sqlSession.commit(); sqlSession.close(); } @Test public void updateUser(){ SqlSession sqlSession = MybatisUtils.**getSqlSession**(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.updateUser(new User(4,"警察局","110")); sqlSession.commit(); sqlSession.close(); } @Test public void deleteUser(){ SqlSession sqlSession = MybatisUtils.**getSqlSession**(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.deleteUser(4); sqlSession.commit(); sqlSession.close(); }
*4.分析常见错误:*
·标签不要出错
·resource绑定mapper,需要使用路径
·报错读错从后往前读
·程序配置文件必须符合规范
·空指针异常(NullPointerException),没有注册到资源
·输出的xml文件中存在中文乱码问题
·maven资源没有导出
*5.万能的Map*
假设:我们的实体类或者数据库中的表,字段或者参数过多,我们应当考虑用Map。
//万能的map int addUser2(Map<String,Object> map);
Xml:
<!--map尝试,传递map的key--> <insert id="addUser" parameterType="map"> insert into mybatis.user(id, name, pwd) VALUES (#{userid},#{username},#{userpwd}); </insert>
测试:
//map测试 @Test public void addUser2(){ SqlSession sqlSession = MybatisUtils.**getSqlSession**(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String,Object>map=new HashMap<String,Object>(); map.put("userid",5); map.put("username","阿巴"); map.put("userid","777777"); mapper.addUser2(map); sqlSession.commit(); sqlSession.close(); }
Map传递参数,直接在sql中取出key即可
parameterType="map"
对象传递参数,直接在sql中取对象的属性即可
parameterType="object"
只有一个基本类型参数的情况下,可以直接在sql中取到
多个参数用map或者注解
*6.模糊查询*
1.Java代码执行的时候,传递通配符%%
List<User> userList = mapper.getUserLike("%李%");
2.在sql拼接中使用通配符
select * from mybatis.user where name like “%”{value}”%”;
*7.配置解析*
·mybatis-config.xml
·Mybatis的配置文件包含了会深深影响Mybatis行为的设置和属性信息
标签的顺序不能打乱
\1. configuration(配置)
\2. Properties(属性)
\3. Settings(设置)
\4. typeAliases(类型别名)
\5. typeHandlers(类型处理器)
\6. objectFactory(对象工厂)
\7. Plugins(插件)
\8. environments(环境配置)
\9. Environment(环境变量)
\10. transactionManager(事务管理器)
\11. dataSource(数据源):dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
\12. databaseIdProvider(数据库厂商标识)
\13. mappers(映射器)
*环境变量*
MyBatis 可以配置成适应多种环境
*不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。*
*学会使用多套运行环境*
Mybatis默认的事务管理器就是JDBC,连接池:POOLED
*属性(properties)*
我们可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
编写一个配置文件:
db.properties driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&uxdunicode=true&serverTimezone=Hongkong&characterEncoding=UTF-8&autoRecpnnect=true username=root password=753123
在核心配置文件中映入
如果在properties里也写了配置,其里面的配置优先,但是resource指定的外部文件配置会覆盖原有配置,所以最终的结果是显示外部配置文件中的配置。
*类型别名(typeAliases)*
·类型别名是为JAVA类型设置一个短的名字
·存在的意义仅在于用来减少类完全限定名的冗余。
<typeAliases> <typeAlias type="com.mybatis.pojo.User" alias="User"/> </typeAliases>
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
扫描实体类的包,它的默认别名就为这个类的类名首字母小写
<typeAliases> <package name="com.mybatis.pojo"/> </typeAliases>
在实体类比较少的情况下使用第一种,若实体类多则使用第二种
第一种可以DIY别名,第二种不能(除非用注解,在实体类上注解)
import org.apache.ibatis.type.Alias; //实体类 @Alias("user")
*设置(settings)*
日志实现
*其他配置*
·mybatis-generator-core
·mybatis-plus
·通用mapper
*映射器(mappers)*
MapperRegistry:注册绑定我们的Mapper文件
方式一:{推荐使用}
<!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册--> <mappers> <mapper resource="com/mybatis/dao/UserMapper.xml"/> </mappers>
方式二:使用class文件绑定注册
!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册--> <mappers> <mapper class="com/mybatis/dao/UserMapper"/> </mappers>
注意点:
·接口和他的Mapper配置文件必须同名
·接口和他的Mapper配置文件必须在用一个包下
方式三:使用扫描包进行注入绑定
!-- 每一个Mapper.xml都需要在Mybatis核心配置文件中注册--> <mappers> <mapper name="com/mybatis/dao"/> </mappers>
注意点:
·接口和他的Mapper配置文件必须同名
·接口和他的Mapper配置文件必须在用一个包下
8. *生命周期和作用域*
生命周期和作用域是至关重要的,因为错误的使用会导致非常严重的*并发问题*
*SqlSessionFactoryBuilder:*
·一旦创建了SqlSessionFactory,就不需要它了
·局部变量
*SqlSessionFactory:*
·说白了就是可以想象为数据库连接池
·SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,*没有任何理由丢弃它或重新创建另一个实例。*
·因此 SqlSessionFactory 的最佳作用域是应用作用域。
·最简单的就是使用单例模式或者静态单例模式。
*SqlSession:*
*·*连接到连接池的一个请求
·SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
·用完之后需要赶紧关闭,否则资源被占用
9. *解决属性名与字段名不一致的问题*
*(1)问题*
数据库中的字段
新建一个项目,拷贝之前的,测试实体类字段不一致的情况
public class User { private int id; private String name; private String password;
测试
出现问题
// select * from mybatis.user where id = #{id}; //类型处理器 // select id,name,pwd from mybatis.user where id = #{id};
解决方法:
·取别名
<select id="getUserById" parameterType="int" resultType="user"> select id,name,pwd as password from mybatis.user where id = #{id}; </select>
(2)**resultMap**
结果集映射
Id name pwd
Id name password
<!--结果集映射--> <resultMap id="UserMap" type="User"> <!-- cilumn数据库中的字段 property实体类中的属性--> <result column="id" property="id"/> <result column="iname" property="name"/> <result column="pwd" property="password"/> </resultMap> <select id="getUserById" parameterType="int" resultMap="UserMap"> select * from mybatis.user where id = #{id}; </select>
·resultMap 元素是 MyBatis 中最重要最强大的元素。
·resultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
·如果这个世界总是这么简单就好了。
10. *日志*
1. 日志工厂
如果一个数据库操作出现了异常,需要排错,日志是最好的助手
曾经:sout,debug
现在:日志
·SLF4J
| LOG4J
| LOG4J2
| JDK_LOGGING
| COMMONS_LOGGING
| STDOUT_LOGGING
| NO_LOGGING
具体使用哪一个在设置中设定
STDOUT_LOGGING标准日志输出
2. Log4j
什么是Log4j?
·Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
·我们也可以控制每一条日志的输出格式
·通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
·通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
(1)先导入Log4j的包
<dependencies> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> </dependencies>
(2)Log4j.properties
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
(3)配置Log4j为日志的实现
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
(4)简单使用:
在log4j的类中导入包import org.apache.log4j.Logger;
日志对象:参数为当前类的class
Static Logger logger=Logger.getLogger(userDaoTest.class);
日志级别:
logger.info("info:进入了testLog4j"); logger.debug("debug:进入了testLog4j"); logger.error("error:进入了testLog4j");
(5)使用结果
(6)测试
11. *分页*
思考:为什么分页?
·减少数据的处理量
使用Limit分页
语法:Select * from user limit startindex,pageSize;
语法:Select * from user limit n;#指0,n
使用mybatis实现分页,核心sql
1. 接口
//分页实现 List<User> getUserByList(Map<String,Integer> map);
2. Mapper.xml
<!--分页实现--> <select id="getUserByList" parameterType="map" resultMap="UserMap"> Select * from mybatis.user limit #{startIndex},#{pageSize}; </select>
3. 测试
@Test public void getUserByList(){ SqlSession sqlSession=MybatisUtils.**getSqlSession**(); UserMapper userMapper=sqlSession.getMapper(UserMapper.class); HashMap<String,Integer> map=new HashMap<String,Integer>(); map.put("startIndex",0); map.put("pageSize",2); List<User> userList = userMapper.getUserByList(map); for (User user : userList) { System.**out**.println(user); } }
12.使用注解开发
1.注解在接口上实现
@Select("select * from book") List<Book> getBooks();
2.需要在核心配置文件绑定接口
<!-- 绑定接口--> <mappers> <mapper class="com.mybatis.dao.BookMapper"/> </mappers>
3.测试
@Test public void test1(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BookMapper mapper = sqlSession.getMapper(BookMapper.class);//底层反射获取包的全类名,获得注解,获得sql语句,然后执行 List<Book> books = mapper.getBooks(); for (Book book : books) { System.out.println(book); } sqlSession.close(); }
本质
反射机制实现
底层
动态代理
增删改查
我们可以在工具类创建的时候自动提交事务
public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(true);//自动提交事务,若没有设置,数据库不会有反应的 }
编写接口,添加注解
@Select("select * from book") List<Book> getBooks(); //方法存在多个参数,所有参数前面都必须加上@Param注解,且Param里的id对应的是sql语句的#{} @Select("select * from book where id = #{id}") Book getBookById(@Param("id") int id); @Insert("insert into Book(id,title,author,publication_date,price) values (#{id},#{title},#{author},#{publication_date},#{price})") int addBook(Book book); @Update("update book set id=#{id},title=#{title},author=#{author},publication_date=#{publication_date},price=#{price} where id=#{id}") int updateBook (Book book); @Delete("delete from book where id=#{id}") int deleteBook(@Param("id") int id);
测试类:
//查询全部 @Test public void test1(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BookMapper mapper = sqlSession.getMapper(BookMapper.class);//底层反射获取包的全类名,获得注解,获得sql语句,然后执行 List<Book> books = mapper.getBooks(); for (Book book : books) { System.out.println(book); } sqlSession.close(); }
注意:我们必须要将接口注册绑定到我们的核心配置文件中
关于@Param()注解
·基本类型的参数或者String类型,需要加上
·引用类型不需要加
·如果只有一个基本类型的话,可以忽略,但建议大家都加上
·我们在SQL中引用的就是我们这里的@Param(“xx”)中设定的属性名
即xx和属性名相同
#{}和${}的区别
#是将传入的值当做字符串的形式,eg:select id,name,age from student where id =#{id},当前端把id值1,传入到后台的时候,就相当于 select id,name,age from student where id ='1'.
$是将传入的数据直接显示生成sql语句,eg:select id,name,age from student where id =${id},当前端把id值1,传入到后台的时候,就相当于 select id,name,age from student where id = 1.
使用#可以很大程度上防止sql注入
但是如果使用在order by 中就需要使用 $.
13.Lombok
提供的注解:
@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
使用步骤:
1.安装Lombok插件,idea自带了已经
2.在项目中导入Lombok的jar包
@Data
这个注解提供:无参构造,get,set,tostring,hashcode,equals
@AllArgsConstructor
提供 有参构造,但是用上这个之后Data的无参构造会消失
显式的定义了有参构造,这时无参构造需要去手动赋值
@NoArgsConstructor
这个就是手动赋值无参构造的注解
14.多对一处理
例子:多个学生对应一个老师
·对于学生而言,关联..多个学生关联一个老师【多对一】
·对于老师而言,集合..一个老师有很多学生【一对多】