一、框架介绍
1. 框架的作用
将许多项目封装起来,形成了框架
2. 框架的优缺点
1. 优点
1.1 更好用
框架都是对Java原生内容进行的封装,对于企业级项目开发来说,使用框架比使用原生Java更好用,写起来更简单。
1.2 更强大
框架封装过程中会内置一些常见必要逻辑或功能,所以在使用框架时很多能够不需要再次编码,框架本身就带有这些功能了。
1.3 开发周期更短
由于框架使用起来更加简单,必定会在一定程度上缩短程序的开发周期。很多IT公司最大的成本都是人员成本,缩短了项目的开发周期,对于企业来说利润更高。这也是企业为什么都喜欢使用框架的最主要原因之一。
2. 缺点
2.1 更多的学习成本
不同的框架由不同的公司或组织开发与维护,不同公司或组织有着不同的习惯和规则,想要使用框架就需要学习框架如何使用,学习这些框架里面的要求。
2.2 初学者更容易出错
因为框架里面都封装了很多内容,想要使用框架就必须按照框架的规则进行使用。对于初学者如果不去记忆里面的规则,自己随意发挥,很容易出错。
所以想要错误更少,学习框架时必须严格按照老师讲的去做,严格按照框架规则去使用。
2.3 对于初学者,出了错误更难解决
因为框架都是深度封装,对于开发者来说可能只写了一行代码,但这一行代码里面可能是几百或几千行代码的封装。一旦报错了,如果不知道框架原理是不太好解决的。
所以想要更好的解决出现的问题,就必须要跟着老师学习框架的原理,同时要对每次出现的错误进行整理记忆,下次在出现这个错误的时候就可以快速解决了。
4. Java中的常见框架
Java项目无论是否使用框架,多采用MVC开发模型,一个项目被分为多层。一个Java框架可能只负责里面的一层。
一个Java项目并不是使用一个框架就可能完成的,通常需要很多框架配合使用,才能实现一个完整的项目。
常见Java框架分类(不仅仅就这些,列举一些常见的):
(1)持久层框架:MyBatis、Hibernate、Spring Data、iBatis。
(2)MVC框架:Spring MVC、Struts1、Struts2。
(3)项目管理框架:Spring Framework、Spring Boot。
(4)微服务框架:Spring Cloud。
(5)权限管理框架:Spring Security、Shiro。
并不是每个项目都需要把上面所有分类都使用上,不同的项目可能只会使用里面几个分类,但是只要使用了其中某个分类,只会选择其中一个。
常见的组合:
(1)SSM:Spring Framework + Spring MVC + MyBatis。最常见组合,属于Java程序员必须掌握的内容。
(2)SSMP:Spring Framework + Spring MVC + MyBatis+MyBatis Plus。对SSM的增强,减少SQL的编写。
(3)SSI:Spring Framework + Spring MVC/Struts1/Struts2 + iBatis。SSM的上代组合,目前很少出现。
(4)SSH:Spring Framework + Struts1/Struts2 + Hibernate。和SSI属于同时代产品,只有在老项目维护时可能出现的技术栈。
(5)Spring Boot + Spring Cloud。微服务架构项目常用组合。
(6)Spring Boot+Mybatis+MybatisPlus。新项目最先选择,比SSM组合使用起来更加方便。
(6)Spring Boot + Shiro/Spring Security。具有权限控制时的组合。
(7)SSM + Shiro/Spring Security。具有权限控制时的组合。
二、软件分层开发
1. Java EE 三层模型 和 MVC模型
Java EE 三层模型和MVC模型属于两种分层模型,一些同学可能认为这是同一个概念,其实并不然。
Java EE中最经典的是三层模型。包含表示层(Presentation)、业务逻辑层(Business Logic)、持久层(Persistence)
MVC 模型也是三层模型。包含模型层(Model)、视图层(View)、控制层(Controller)。
Java EE 三层模型和MVC模型最主要的区别是:
(1)Java EE三层模型中没有控制层,MVC中有控制层。
(2)Java EE三层模型中业务模型层是单独一部分,就是service层,MVC中模型层包含:业务模型(业务逻辑层)和数据模型(实体类,持久层)。
(3)Java EE三层模型中持久层就是dao层。MVC中虽然持久层在项目中是单独的包,但是在MVC概念中持久层属于模型层中。
2. 目前Java中最常见的六层模型
目前在软件开发过程中最常见的是六层模型,我们讲解的也是六层模型:
(1)视图层。简单点说就是页面,可以是客户端页面技术,也可以是服务端页面技术。例如:HTML、JSP。
(2)控制层。处于业务层和视图层之间,根据业务层结果,控制视图层显示。例如:Servlet。
(3)实体层。就是实体类,负责封装数据,在各个层之间进行数据传递的载体。常见包名:pojo、domain、entity等。
(4)业务逻辑层。专门编写业务逻辑代码。
(5)数据持久层/数据访问层。负责编写调用数据库的代码。具体技术:JDBC、MyBatis等。
(6)数据源层。一些持久化工具,负责存储数据的。例如:MySQL、Oracle等。
3. ORM
ORM(Object/Relation Mapping),中文名称:对象/关系 映射。是一种解决数据库发展和面向对象编程语言发展不匹配问题而出现的技术。
使用JDBC技术时,手动实现ORM映射:
ORM框架封装了对象/关系的自动映射。
6.常见的ORM框架
对于Java程序员来说,整个职业生涯可能听说过或可能用上的ORM框架:
(1)MyBatis:目前使用最多的ORM框架。
(2)Hibernate:零SQL的ORM框架,N年前使用最多的ORM框架。目前只能在一些老的项目中看到。
(3)Spring Data JPA:目前个别公司中使用的ORM框架。是Spring Data家族中的一员。是对JPA(Java Persistence API,JDK5.0)的封装。
(4)Spring Data JDBC:Spring Data中的二级项目,类似Spring Data JPA,但框架的功能要比Spring Data JPA少一些。
三、MyBatis介绍
1. 介绍
MyBatis 是一款优秀的ORM框架,MVC分层开发中的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
四、第一个MyBatis项目
2. 创建Maven项目并添加依赖
创建一个Maven项目(示例中叫做:maven1),并配置pom.xml。
需要添加MyBatis的依赖和MySQL驱动的依赖。(文档制作时MyBatis最新版本为3.5.9,mysql驱动最新版本为8.0.29)因为MyBatis的底层也是MySQL,JDBC实现的
<?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>com.sh</groupId><artifactId>MyBatis01</artifactId><version>1.0-SNAPSHOT</version><dependencies><!-- MyBatis 框架依赖 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.9</version></dependency><!-- 数据库驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version></dependency></dependencies></project>
3. 创建MyBatis全局配置文件
在项目的src/main/resources目录下新建mybatis.cfg.xml文件。并在配置文件中填写下面内容。
配置文件解释:
-
XML文档头是XML文件必须有的。
-
DOCTYPE声明文档类型,引入外部约束。
-
<configuration>
是根节点。 -
<environments>
的default属性取值必须是下面某个<environment>
标签的id属性值。表示当前MyBatis框架到底使用哪个环境。 -
<environment>
的id值是自定义的。表示当前数据库环境的名称。在全局配置文件中<environment>
标签可以有多个,里面分别配置上数据源相关参数。例如一个里面配置连接本地MySQL的参数,另一个配置连接服务器中MySQL的参数,还有一个配置连接Oracle数据库的相关参数。都准备好了以后可以通过修改<environments>
中default的值来切换连接的数据库,这样要比修改里面具体的连接参数更加方便。 -
<transactionManager>
中type属性用于配置事务的类型。可取值:-
JDBC:使用原生JDBC的事务管理进行提交和回滚。
-
MANAGED:MyBatis不参与事务管理。交给其他人管理事务。
-
-
<dataSource>
中type属性值用于配置数据源类型。可取值:-
UNPOOLED:不使用数据库连接池。每次执行SQL都会打开数据库连接,执行SQL结束都会关闭连接。适用于简单小型应用。
-
POOLED:使用MyBatis内置数据库连接池。对于频繁访问数据库的应用程序来说,这个取值是很适合的。
-
JNDI:使用本地目录接口技术连接容器外部的数据源。这种方式很少使用,一般都是极其复杂的项目,对数据源要求极高的情况下才能使用。
-
-
driver、url、username、password四个属性名称是固定的,但是没有顺序要求,添加到<properties>
配置具体属性和值<properties>
的name属性中即可。分别代表驱动类、连接字符串、用户名、密码。value属性的值为连接数据库的具体参数值。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!-- 配置执行的数据库环境 default中是默认使用哪个数据库技术--><!-- default中的名字也可以叫别的--><environments default="mysql"><environment id="mysql"><!--事务控制器有两种1. JDBC : 使用原生的JDBC的事务管理2. MANAGED : MyBatis不参与事务管理,交给其他人管理--><transactionManager type="JDBC"></transactionManager><!--配置数据源有三种UPOOLED: 不使用数据库连接池POOLED : 使用数据库连接池JNDI : 使用本地目录接口技术,特殊情况使用--><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments>
</configuration>
4. 创建Mapper映射文件
在src/main/resources目录中新建mybatis目录。这个文件夹可以新建也可以不建立,名称也是随意的。以后mapper文件会比较多,建立一个文件夹项目结构比较好看,名称叫做mybatis也好记。
在mybatis文件夹下新建xml文件,文件名称随意。示例中就叫做mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 配置SQL映射的文件 -->
<mapper namespace="a.b"><insert id="addUser">insert into people values(default ,'zs','123')</insert>
</mapper>
5. 配置加载mapper映射文件
映射文件是不能默认被加载的。需要在全局配置文件mybatis.cfg.xml中手动指定加载路径。
6. 编写测试类,启动项目
package com.sh;import static org.junit.Assert.assertTrue;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 org.junit.Test;import java.io.IOException;
import java.io.InputStream;/*** Unit test for simple App.*/
public class AppTest {/*** Rigorous Test :-)*/@Testpublic void test1() throws IOException {// 1. 获取全局配置文件输入流InputStream is = Resources.getResourceAsStream("mybatis.cfg.xml");// 2. 加载全局配置文件后创建工厂类SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);// 3. 使用工厂创建SqlSession. 里面封装了所有增删改查的方法SqlSession session = factory.openSession();// 4. 执行sql方法,里面传递 namespace.id 通过语句的唯一标识找到sql语句int index = session.insert("a.b.addUser");System.out.println(index);// 5. mybatis是手动提交session.commit();// 6. 关闭连接,释放资源session.close();}
}
五、MyBatis属性加载
MyBatis支持加载属性文件(.properties文件),可以通过在属性文件中配置数据库连接属性然后加载。这种方式要比直接写稍微麻烦一点点,但是却把所有的数据库连接书写到了统一的文件中,以后查看或修改时更加方便。
1. 创建属性文件
在src/main/resources目录中创建jdbc.properties文件
注意:需要将$amp;转义字符变为&,否则运行报错
jdbc.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=root
2. 修改全局配置文件
修改mybatis.cfg.xml文件,设置加载属性。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><!-- 加载属性文件 ,再给属性赋值 --><properties resource="jdbc.properties"></properties><!-- 配置执行的数据库环境 default中是默认使用哪个数据库技术--><!-- default中的名字也可以叫别的--><environments default="mysql"><environment id="mysql"><!--事务控制器有两种1. JDBC : 使用原生的JDBC的事务管理2. MANAGED : MyBatis不参与事务管理,交给其他人管理--><transactionManager type="JDBC"></transactionManager><!--配置数据源有三种UPOOLED: 不使用数据库连接池POOLED : 使用数据库连接池JNDI : 使用本地目录接口技术,特殊情况使用--><!--<dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true"/><property name="username" value="root"/><property name="password" value="root"/></dataSource>--><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments><mappers><mapper resource="mybatis/mapper.xml"></mapper></mappers>
</configuration>
五、常见Java日志工具包
1. 日志介绍
日志是项目开发非常重要的一项。项目部署到服务器以后,并不能所有信息都通过控制台或命令窗口进行查看。把日志信息输入到文件中更有利于项目的维护。
Java项目的日志是可以输入到控制台、可以输入到文件中、甚至可以输入到某个数据源中。但是常用的还是控制台和文件。
在Java项目中常见的日志工具包:
-
Log4j:Apache推出的日志工具。于2012年5月发布1.2.17版本后停止更新。
-
Logback:属于Log4j的继承者。Spring Boot默认日志文件支持类型。
-
Log4j2:属于Log4j升级版,同时里面还包含了Logback的很多改进。
-
commons-logging:Apache早期基于门面(Facade)设计模式的日志包,提供了日志解构能力,按照顺序寻找当前项目日志接口实现,Log4j、JDK Log、SimpleLog。
-
Slf4j( Simple Logging Facade for Java ):目前使用非常多的门面日志。统一项目中日志。
-
JDK Logging:JDK自带的日志API。
2. JDK Logging
JDK Logging 是JDK自带的日志工具。存在于java.uti.logging包中。
日志的级别: OFF > SEVERE > WARNING > INFO > CONFIG > FINE > FINER > FINEST > ALL
注意:
1. 默认级别为INFO。
2. 只会显示该级别及该级别以上日志信息。
package com.sh;import java.io.IOException;
import java.util.logging.*;public class Demo {public static void main(String[] args) throws IOException {//参数通常设置为所在的类名Logger logger = Logger.getLogger("Demo");// 默认为INFO级别,同时设置当前和父处理器级别才能修改// OFF > SEVERE > WARNING > INFO > CONFIG > FINE > FINER > FINEST > ALLlogger.setLevel(Level.FINEST);Handler[] handlers = logger.getParent().getHandlers();// 获取输出控制台的处理器handlers[0].setLevel(Level.FINEST);// 设置控制台输出级别// 默认没有输出到文件,只输出到控制台,通过添加Handler增加输出目的地,输出到文件中FileHandler fh = new FileHandler("my.log");// 如果路径夹会不存在文件夹会报错fh.setFormatter(new SimpleFormatter());// 设置输出内容为普通格式logger.addHandler(fh);logger.severe("severe");// 严重logger.warning("warning");// 警告logger.info("info");// 信息logger.config("config");// 配置logger.fine("fine");// 详细logger.finer("finer");// 较详细logger.finest("finest");// 非常详细}
}
3. Log4j的使用
Log4j是Apache的开源日志工具。最新版本为2012年发布的1.2.17。
Log4j会自动寻找classpath下log4j.properties作为配置文件。
具体使用步骤:
3.1 配置pom.xml
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>
3.2 编写配置文件
在resources中新建log4j.properties,名称不要写错了。
log4j中定义的级别:fatal(致命错误) > error(错误) > warn(警告) > info(普通信息) > debug(调试信息) > trace(跟踪信息)
# log4j中定义的级别:fatal(致命错误) > error(错误) > warn(警告) > info(普通信息) > debug(调试信息) > trace(跟踪信息)
# 跟日志等级为debug 使用console和file两种方式输出
log4j.rootLogger = DEBUG , console , file### console ###(日志显示在控制台的配置)
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = [%p] [%-d{yyyy-MM-dd HH\:mm\:ss}] %C.%M(%L) | %m%n### log file ###(日志输出在文件中的配置)
log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
# 日志输出的路径
log4j.appender.file.File = log4j.log
# 是否给日志文件追加
log4j.appender.file.Append = true
# 级别只能比根日志界别高,比根日志级别低不生效
#log4j.appender.file.Threshold = info
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = [%p] [%-d{yyyy-MM-dd HH\:mm\:ss}] %C.%M(%L) | %m%n
3.3 编写测试类
注意:千万不要倒错包。
package com.sh;
// 不要导错包
import org.apache.log4j.Logger;public class TestLog4j {public static void main(String[] args) {Logger logger = Logger.getLogger(TestLog4j.class);logger.trace("跟踪级别");logger.debug("调试信息");logger.info("普通信息");logger.warn("警告信息");logger.error("错误信息");logger.fatal("重大错误信息");}
}
4. Log4j2
4.1 配置pom.xml
虽然只导入了log4j-core,但是实际导入log4j-core和log4j-api。
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.17.2</version>
</dependency>
4.2 编写配置文件
Log4j2不再支持Log4j的.properties格式配置文件。而是支持.xml、.json、jsn。
Log4j2中定义的级别:fatal(致命错误) > error(错误) > warn(警告) > info(普通信息) > debug(调试信息) > trace(跟踪信息)
在resource目录新建log4j2.xml。
<?xml version="1.0" encoding="utf-8" ?>
<Configuration ><!-- 定义输出的目的地 --><Appenders><!-- Console 定义控制台输出 name:自定义名称 target:输出方式,支持SYSTEM_OUT SYSTEM_ERR --><!-- SYSTEM_OUT 正常输出内容SYSTEM_ERR 错误高亮输出内容--><Console name="console" target="SYSTEM_OUT"><!-- 输出信息表达式 --><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console><!-- File 定义文件输出 fileName 文件路径 append是否允许追加 --><File name="file" fileName="log4j2.log" append="true"><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></File></Appenders><Loggers><!-- 控制总体级别 --><!-- fatal > error > warn > info > debug > trace --><Root level="info"><!-- 引用输出位置的配置,Appenders中直接子标签的name属性值 --><!-- 映射哪种输出模式 --><AppenderRef ref="console"/><AppenderRef ref="file"/></Root></Loggers>
</Configuration>
4.3 编写测试类
package com.bjsxt;
// 千万别导错包了
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;public class TestLog4j {public static void main(String[] args) {Logger logger = LogManager.getLogger(TestLog4j.class);logger.trace("跟踪级别");logger.debug("调试信息");logger.info("普通信息");logger.warn("警告信息");logger.error("错误信息");logger.fatal("重大错误信息");}
}
5. SLF4j
SLF4j是日志的接口声明,不参与日志的具体实现,需要配合其它日志具体实现工具包才能进行使用。
每次添加其它日志工具包时,不要忘记SLF4j整合这个日志的依赖。
5.1 整合Log4j
5.1.1 导入依赖
导入3个依赖,分别代表slf4j依赖、整合依赖、log4j的依赖。
<!--slf4j整合log4j的依赖,版本要和slf4j的版本对应 -->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.36</version>
</dependency>
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>
5.1.2 编写log4j的配置文件
使用写号的log4j.properties文件就行
5.1.3 编写测试类
小提示:
SLF4J作为门面设计模式的具体实现,无论使用哪种日志具体实现,都是下面的API。
public class TestSlf4j {public static void main(String[] args) {Logger logger = LoggerFactory.getLogger(TestSlf4j.class);logger.trace("trace");logger.debug("debug");logger.info("info");logger.warn("warn");logger.error("error");}
}
5.2 整合Log4j2
5.2.1 配置pom.xml
与SLF4J整合Log4j的整合包是不一样的。
<dependencies><!-- SLF4j整合Log4j2的依赖 --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.17.2</version></dependency><!-- Log4j2工具的依赖--><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.17.2</version></dependency>
</dependencies>
5.2.2 添加Log4j2的配置文件
使用之前的log4j2.xml文件。
5.2.3 运行测试类
测试类的API和整合Log4j的API是完全相同的,直接运行即可。
5.3 即整合Log4j又整合Log4j2
SLF4J支持多种日志实现,但是在项目中整合依赖最好只有一个,否则会出现警告。
六、MyBatis启用日志功能
1. MyBatis支持的日志
MyBatis框架内置日志工厂。日志工厂负责自动加载项目中配置的日志。MyBatis支持以下日志,当存在多个日志工具时,严格按照从上往下顺序使用,且只会使用一个。
-
SLF4J
-
Apache Commons Logging
-
Log4j 2
-
Log4j (deprecated since 3.5.9)
-
JDK logging
2. MyBatis结合Log4j实现打印执行的SQL
3. MyBatis结合Log4j2实现打印执行SQL
同理演示Log4j2
3.1 编写pom.xml,引入依赖
<dependencies><!-- MyBatis 框架依赖 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.9</version></dependency><!-- 数据库驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version></dependency><!-- log4j2 --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.17.2</version></dependency>
</dependencies>
3.2 创建配置文件
在resources目录下新建log4j2.xml文件
<?xml version="1.0" encoding="utf-8" ?>
<Configuration ><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root><!-- namespace的值 --><logger name="a.b" level="debug"></logger></Loggers>
</Configuration>
4. 指定生效的日志
在MyBatis中默认按照顺序寻找,如果项目中存在多个日志。可以通过mybatis全局配置文件进行设置哪个日志生效。
在mybatis.cfg.xml中配置<settings>
的标签
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><properties resource="jdbc.properties"></properties><!-- 配置哪个日志生效 , 多个日志的时候设置 --><!--<settings><setting name="log4j2" value="LOG4J2"/></settings>--><!-- 配置执行的数据库环境 default中是默认使用哪个数据库技术--><!-- default中的名字也可以叫别的--><environments default="mysql"><environment id="mysql"><!--事务控制器有两种1. JDBC : 使用原生的JDBC的事务管理2. MANAGED : MyBatis不参与事务管理,交给其他人管理--><transactionManager type="JDBC"></transactionManager><!--配置数据源有三种UPOOLED: 不使用数据库连接池POOLED : 使用数据库连接池JNDI : 使用本地目录接口技术,特殊情况使用--><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url"value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments><mappers><mapper resource="mapper.xml"></mapper></mappers>
</configuration>
5. 测试
package com.sh;import static org.junit.Assert.assertTrue;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 org.junit.Test;import java.io.IOException;
import java.io.InputStream;/*** Unit test for simple App.*/
public class AppTest {/*** Rigorous Test :-)*/@Testpublic void test1() throws IOException {//1.通过流读取全局配置文件InputStream is = Resources.getResourceAsStream("mybatis_config.xml");//2.创建工厂SqlSessionFactory构建器然后创建工厂SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);//3.通过工厂创建SqlSessionSqlSession session = factory.openSession();//4.session中封装了所有的增删改查的方法int i = session.insert("a.b.add1");//验证结果System.out.println(i);//5. 增删改手动提交事务session.commit();//6. 释放资源session.close();}
}
七、SQL参数
1. MyBatis中的占位符
MyBatis中最常用的占位符为 #{},在MyBatis的mapper文件的SQL中使用#{}作为占位符。最终解析时会解析成JDBC的占位符?。
2. 当参数为简单数据类型
当参数为一个简答数据类型时。例如:八大基本数据类型、String类型等。可以通过#{任意内容}获取到。
小提示:
1. 任意内容代表着,随意写,只要不是特殊字符即可。
2. 但是要注意不能不写。
2.1 修改Test类
SqlSession的insert()方法有两种。
想要传递参数就需要使用insert(String,Object)的方法。
@Testpublic void test2(){SqlSessionFactory factory = getFactory();//3.通过工厂创建SqlSessionSqlSession session = factory.openSession();int i = session.insert("a.b.add2","456");System.out.println(i);session.commit();session.close();}
<insert id="add2">insert into user values (default ,'ls',#{pwd})</insert>
通过使用,使用#{} 给占位赋值 先将#{}变成? 再进行解析 只有一个值需要赋值时
#{}括号中写什么都行
3. 当参数为多个值时
3.1 修改Test类
由于insert(String,Object)方法只有一个Object类型方法能作为SQL参数,如果希望传递多个值,有两种选择:
1.可以创建一个对应类,通过对象进行传递。
参数是对象通过${属性名}获取
@Testpublic void test3(){SqlSessionFactory factory = getFactory();//3.通过工厂创建SqlSessionSqlSession session = factory.openSession();User user = new User();user.setUsername("zw");user.setPassword("789");int i = session.insert("a.b.add3",user);System.out.println(i);session.commit();session.close();}
2.也可以把所有SQL参数放入到一个Map中。
参数是Map,通过${key}获取
@Testpublic void test3(){SqlSessionFactory factory = getFactory();//3.通过工厂创建SqlSessionSqlSession session = factory.openSession();/* User user = new User();user.setUsername("zw");user.setPassword("789");*/HashMap<String, String> map = new HashMap<>();map.put("username","zw");map.put("password","147");int i = session.insert("a.b.add3",map);System.out.println(i);session.commit();session.close();}
3.2 mapper文件
<insert id="add3">insert into user values (default ,#{username},#{password})</insert>
4. ${} 的使用
在MyBatis中还有一种占位符写法:${}。
${}在被MyBatis进行解析时,不会解析为占位符?,而是直接解析成对应的值,类似字符串拼接。
学习JDBC时我们知道SQL中存在字符串拼接时,会有SQL注入问题。
那是不是意味着${}没有使用场景了?答案是否定的。JDBC中占位符?是不允许代替列名和表名的,也就意味着MyBatis的#{}不允许代替表名和列名,但是使用${}可以动态设置列名或表名。
${}和#{}获取参数的写法完全相同。如果参数是对象通过${属性名}获取。如果参数是Map,通过${key}获取。如果参数是简单数据类型,通过${任意不为空的内容}进行获取。
4.1 修改映射文件
${}和#{}可以混合使用。实例中使用${}代替表名。
<insert id="add4">insert into ${table} values (default ,#{username},#{password})</insert>
4.2 修改Test类
@Testpublic void test4(){SqlSessionFactory factory = getFactory();SqlSession session = factory.openSession();HashMap<String, String> map = new HashMap<>();map.put("table","user");map.put("username","zw");map.put("password","258");int i = session.insert("a.b.add4", map);System.out.println(i);session.commit();session.close();}
通过日志可以看到
${}直接解析为值 , 而#{}则被解析为?
八、MyBatis中修改和删除实现
1. 修改和删除功能总体说明
MyBatis实现修改和删除功能与实现新增时是非常类型的,SQL参数传递的几种方式也是相同的。
主要区别:
就是mapper文件中标签名不同。
SqlSession的方法名不同。
操作 | mapper标签 | SqlSession方法名 |
---|---|---|
新增 | <insert> | insert(String)、insert(String,Object) |
修改 | <update> | update(String)、update(String,Object) |
删除 | <delete> | delete(String)、delete(String,Object) |
2. 修改实现
2.1 修改映射文件
修改suiyi.xml文件.在里面添加上<update>
标签。设定使用Map进行传递参数了,所以直接使用#{key}获取到参数。
<update id="update1">update user set username=#{username} where id=#{id}</update>
2.2 修改测试类
@Testpublic void test5(){SqlSessionFactory factory = getFactory();SqlSession session = factory.openSession();HashMap<String, String> map = new HashMap<>();map.put("username","hhh");map.put("id","1");int i = session.update("a.b.update1", map);System.out.println(i);session.commit();session.close();}
3. 删除实现
3.1 修改映射文件
修改suiyi.xml文件.在里面添加上<delete>
标签。设定使用Map进行传递参数了,所以直接使用#{key}获取到参数。
<delete id="delete1">delete from user where id=#{id}</delete>
3.2 修改测试类
/* 删除实现 */@Testpublic void test6(){SqlSessionFactory factory = getFactory();SqlSession session = factory.openSession();int i = session.delete("a.b.delete1", 10);System.out.println(i);session.commit();session.close();}
九、MyBatis中DQL操作
MyBatis的DQL操作在映射文件都是通过<select>
标签实现的。
SqlSession根据根据查询结果类型不同,提供了五种查询方法:
方法名 | 解释说明 |
---|---|
selectOne() | 查询一行数据时,返回值为Object。如果没有查询到,但是不能查询到多行。 |
selectMap() | 查询多行数据时,把其中某列结果当做key,每行结果为Value |
selectList() | 当查询多行数据时,返回值为List。如果没有查询到返回长度为零的List对象。 |
selectCursor() | 使用游标查询时使用,在大量数据时可以代替分页 |
select() | 万能方法,需要自己定义结果处理器 |
2. selectOne()方法使用
selectOne()方法要求查询结果是一行或没有查询到。如果查询结果是多行会报异常。
如果这行数据有多个值,可以放在实体类对象中。如果这行数据只有一列,可以使用简单数据类型接收
2.1 查询到一行或没有查询到结果时
2.1.1 修改映射文件
在映射中添加<select>
标签,resultType属性是<select>
标签必须有的属性,表示查询结果最终类型。取值为类型的全限定路径。就是查询结果对应的类是哪个要指定,而且要使用包名+类名
<!--DQL:selectOne必须保证列名和resultType中类型的属性名相同才能保证把查询到的结果自动放入对象当中
--><select id="one" resultType="com.sh.util.User">select * from user where id = #{id}</select>
2.1.2 修改Test类
selectOne()方法返回值是泛型类型,所以可以直接写对应的类型。
@Testpublic void test7(){SqlSessionFactory factory = getFactory();SqlSession session = factory.openSession();Map<String,Object> map = new HashMap<>();map.put("id",1);// 使用selectOne方法进行查询// 返回值可以直接写映射文件中select的resultType对应类型//知道是user类型直接转换成user类型User user = session.selectOne("a.b.select1", map);System.out.println(user);//查询不需要事务的提交/*session.commit();*///释放资源session.close();}
2.1.3 当没有写resultType属性时
resultType是<select>
必须写的属性。如果不写resultType属性执行时会报错。在示例中删除掉了resultType属性。
2.1.4 查询到多行数据时
如果使用selectOne()方法,对应的SQL查询到多行数据会报错。
例如:修改映射文件的SQL为查询全部,且保证数据库people表有2行以上数据
3. selectList()方法使用
selectList()方法主要用在多行查询时,查询时把每行结果按照resultType类型进行封装,最终放入到List中。
3.1 修改映射文件
注意:resultType的类型为每行结果的类型,也就是List的泛型的类型。
<select id="select2" resultType="com.sh.util.User">select * from user</select>
3.2 修改Test类
//查询多个@Testpublic void test8(){SqlSessionFactory factory = getFactory();SqlSession session = factory.openSession();// 使用selectList方法进行查询// 返回值可以直接写映射文件中select的resultType对应类型List<User> list = session.selectList("a.b.select2");System.out.println(list);//查询不需要事务的提交/*session.commit();*///释放资源session.close();}
4. selectMap()方法使用
selectMap(String statement,Object param,String key);比上面多了String key参数。
String key:指定查询结果中哪列的值作为map的key。map的value是整行数据,类型和resultType类型一致。
4.1 修改映射文件
<select id="select3" resultType="com.sh.util.User">select * from user</select>
4.2 修改测试类
修改后运行效果,查看key和value的值
// 使用 selectMap()@Testpublic void test9(){SqlSessionFactory factory = getFactory();SqlSession session = factory.openSession();// 使用selectList方法进行查询// 返回值可以直接写映射文件中select的resultType对应类型Map<String, Object> map = session.selectMap("a.b.select3", "id");System.out.println(map);//查询不需要事务的提交/*session.commit();*///释放资源session.close();}
5. selectCursor()方法使用
cursor 表示游标。属于对查询结果集的一种遍历方式。MyBatis底层使用JDBC,在MyBatis封装的Cursor中只有遍历功能。其实就是一个一个的查询出来
5.1 修改映射文件
<select id="select4" resultType="com.sh.util.User">select * from user</select>
5.2 修改Test类
selectCursor()返回值为Cursor,提供了forEach()遍历和iterator()两种遍历方式,下面使用forEach()遍历方式。
@Testpublic void test10(){SqlSessionFactory factory = getFactory();SqlSession session = factory.openSession();// 使用selectList方法进行查询// 返回值可以直接写映射文件中select的resultType对应类型//泛型知道是userCursor<User> cursor = session.selectCursor("a.b.select4");cursor.forEach(System.out::println);//查询不需要事务的提交/*session.commit();*///释放资源session.close();}
6. select()方法使用
select(String statement,Object param,ResultHandler resultHandler);方法中第三个参数表示结果处理。
在ResultHandler接口中只有一个方法,表示如何处理
//使用select//自定义查询@Testpublic void test11(){SqlSessionFactory factory = getFactory();SqlSession session = factory.openSession();// 使用selectList方法进行查询// 返回值可以直接写映射文件中select的resultType对应类型//该方法是自定义查询,需要重写handleResult方法,可以创建一个类继承ResultHandle来实现//也可以使用匿名内部类,这里使用内部类MyResultHandler myResultHandler = new MyResultHandler();session.select("a.b.select5",myResultHandler);//查询不需要事务的提交/*session.commit();*/System.out.println("-------------------");System.out.println(myResultHandler.getResult());//释放资源session.close();}public class MyResultHandler implements ResultHandler {HashSet<Object> set = new HashSet<>();@Overridepublic void handleResult(ResultContext resultContext) {// System.out.println(resultContext.getResultObject());set.add(resultContext.getResultObject());}public Set<Object> getResult(){return set;}}
十、分页查询
1. MyBatis实现分页查询的几种方式
在MyBatis中实现查询有两种方式:
-
根据对应的数据库,编写SQL。这种方式与数据库耦合度较高,移植性差。但是确实我们平时使用的方式,因为大部分项目是没有修改数据库类型的场景。
-
使用MyBatis提供的RowBounds实现分页
2. 根据对应数据库编写SQL
2.1 修改映射文件
<select id="select6" resultType="com.sh.util.User">select * from user limit #{index},#{size}</select>
2.2 修改测试类
@Testpublic void test12(){SqlSessionFactory factory = getFactory();SqlSession session = factory.openSession();HashMap<String, Object> map = new HashMap<>();map.put("index",0);map.put("size",3);session.selectList("a.b.select6",map);System.out.println(map);//释放资源session.close();}
这是类似JDBC的方法
3. RowBounds方式进行分页
RowBounds是MyBatis中提供的一种"假分页"实现方式。对从数据库中查询到的结果进行截取。所以如果数据库中数据量特别大的时候可能会出现OOM等问题。就是当查询第二页时会把第一页的数据也查询出来,但是最终只截取第二页的内容
但是由于RowBounds不需要考虑底层数据库是什么,且使用简单,所以对于一些数据量不是特别大的应用还是有人选择使用的。
在SqlSession中select、selectMap、selectList中通过方法重载都提供了一个带有RowBounds。
3.1 修改映射文件
<!-- 使用RowBounds --><select id="select7" resultType="com.sh.util.User">select * from user</select>
3.2 修改Test类
/* 使用RowBounds */@Testpublic void test13(){SqlSessionFactory factory = getFactory();SqlSession session = factory.openSession();//创建偏移量对象RowBounds rowBounds = new RowBounds(0, 3);//因为偏移量必须是第三个参数,再不需要给占位符赋值的时候,第二个参数也必须要写,写为nullList<User> list = session.selectList("a.b.select7", null, rowBounds);System.out.println(list);//释放资源session.close();}
十一、模糊查询
1. 使用#{}实现模糊查询
1.1 修改映射文件
<select id="select8" resultType="com.sh.util.User">select * from user where username like #{username}</select>
1.2 修改测试类
// 模糊查询@Testpublic void test14(){SqlSessionFactory factory = getFactory();SqlSession session = factory.openSession();List<User> list = session.selectList("a.b.select8","%s%");System.out.println(list);//释放资源session.close();}