1、框架
1.1、框架定义
框架(Framework) 是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种认为,框架是可被应用开发者定制的应用骨架、模板。
简单的说,框架其实是软件的半成品,就是一组组件, 供你使用完成你自己的系统。它解决了软件过程当中的普遍性问题,从而简化了开发步骤,提高了开发的效率。
框架是安全的,可复用的,它是可以不断升级改造的软件。
框架是对某一个方面有用的,它不是全能的。
1.2、三层架构
mvc:web开发中,使用mvc架构模式。m:数据,v:视图,c:控制器
c控制器:接收请求,调用service对象,显示请求的处理结果,当前使用servlet作为控制器
v视图:使用jsp、html、css、js,显示请求的处理结果,把m中的数据显示出来
m数据:数据库mysql,文件、网络
mvc作用:
- 实现解耦合
- 让mvc各司其职
- 使得系统扩展更好,更容易维护
- 界面层(视图层):接收用户的请求,调用service,显示请求的处理结果,包含了jsp、html、servlet等对象。对应的包controller
- 业务逻辑层:处理业务逻辑,使用算法处理数据的,把数据返回给界面层,对应的service包,和包中的xxxservice类,例(OrderService、Userservice)
- 持久层(数据访问层):访问数据库,或者读取文件,访问网络,获取数据,对应的包是dao。dao包中很多的类(OrderDao、UserDao)
1.3、三层架构请求的处理流程
用户发起请求—>界面层—>业务逻辑层---->持久层---->数据库(mysql)
为什么要使用三层架构?
- 结果清晰、耦合度第,各层分工明确
- 可维护高,可扩展性高
- 有利于标准化
- 开发人员可以只关注整个结构中的其中某一层的功能实现
- 有利于各层逻辑的复用
1.4、三层架构模式和框架
每一层对应着一个框架
- 界面层—>SpringMvc框架
- 业务逻辑层—>Spring框架
- 持久层—>Mybatis框架
Mybatis框架:
MyBatis是一个优秀的基于java的持久层框架,内部封装了jdbc;开发者只需要关注sql语句本身,而不需要处理加载驱动、创建连接、创建statement、 关闭连接
Spring框架:
Spring框架为了解决软件开发的复杂性而创建的。Spring 使用的是基本的JavaBean来完成以前非常复杂的企业级开发。
SpringMVC框架:
Spring MVC属于SpringFrameWork 3.0版本加入的一个模块,为Spring 框架提供了构建Web应用程序的能力。
框架能解决的问题
- 框架能实现技术的整合
- 提供开发效率,减低难度
jdbc访问数据库的优缺点
优点:
- 直观好理解
缺点:
- 创建很多对象 Connection、Statement、RestultSet
- 注册驱动
- 执行sql语句
- 把RestultSet作为Student,List集合
- 关闭资源
- sql语句和业务逻辑代码混在一起
2、MyBatis框架
2.1、什么是MyBatis?
- Mybatis是apache的一个开源项目iBatis, 2010年这个项目由apacheoftware foundation 迁移到了google code, 并且改名为MyBatis 。2013年11月迁移到 Githut
- MyBatis是一个优秀的持久层框架,支持自定义SQL,存储过程和高级映射
- Mybatis对原有的JDBC操作进行了封装,几乎消除了所有JDBC代码,使开发者只需关注SQL本身
- Mybatis可以使用简单的XML或Annotaion来配置执行sql,并自动完成ORM操作,将执行结果返回
ORM框架
- ORM(Object Relational Mapping)对象关系映射,将程序中的一个对象与表中的一行数据一一对应
- ORM框架提供了持久化与表的映射关系,在运行时参照映射文件的信息,将对象持久化到数据库中
Mybatis解决的主要问题
- 减少使用JDBC的复杂性,不用编写重复的创建Connection、Statement;不用编写关闭资源的代码
- 直接使用java对象,表示结果数据。让开发者专注于SQL的处理,其他分心的工作由Mybatis代劳
Mybatis能做什么?
- 注册驱动
- 创建jdbc中使用的Connection、Statement、Restult
- 执行sql语句,得到Restult
- 处理RestultSet,把记录中的数据转为java对象,同时还能把java对象放入list集合中
- 关闭资源
- 实现sql语句的和java代码的解耦合
访问与下载
3、Mybatis入门
思路:搭建环境—>导入Mybatis—>编写代码—>测试!
3.1、搭建环境
CREATE DATABASE IF NOT EXISTS `mybatis`;
USE mybatis;
CREATE TABLE `user`(
`id` INT(20) NOT NULL,
`name` VARCHAR(30) DEFAULT NOT NULL,
`pwd` VARCHAR(30) DEFAULT NOT NULL,
`gender` int(2) DEFAULT NOT NULL,
PRIMARY KEY(id)
)ENGINE = INNODB DEFAULT CHARSET=UTF8;
INSERT INTO `user` VALUES (null,'李思','666666',1);
INSERT INTO `user` VALUES (null,'晓天','111111',0);
INSERT INTO `user` VALUES (null,'小林','888888',1);
INSERT INTO `user` VALUES (null,'李明','666666',0);
INSERT INTO `user` VALUES (null,'小米','777777',1);
-
新建一个普通的Maven项目
-
删除src目录(父工程)
-
导入maven依赖
<!--导入依赖--> <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.6</version> </dependency> <!--junit单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> </dependencies>
3.2、创建一个模块
-
编写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> <!--导入连接外部参数--> <properties resource="db.properties"/> <!--设置--> <settings> <!--标准的日志工厂--> <!--<setting name="logImpl" value="STDOUT_LOGGING"/>--> <setting name="logImpl" value="LOG4j"/> </settings> <!--实体类别名--> <typeAliases> <!--定义类的别名--> <!--<typeAlias type="com.zhang.pojo.User" alias="user"/>--> <!--定义实体类所在的package,每个实体都会自动注册一个别名 = 类名--> <!--自动扫描包,将原类名作为别名--> <package name="com.zhang.pojo"/> </typeAliases> <!--核心配置信息--> <environments default="development"> <!--数据库相关配置--> <environment id="development"> <!--事务控制类型--> <transactionManager type="JDBC"/> <!--配置数据源:创建Connection对象--> <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> <!--mapper注册--> <mappers> <!--注册mapper文件所在的位置:目的是找到其他文件的sql语句--> <!-- 注:resource = "mapper文件的路径,使用 / 分隔" 一个mapper resource指定一个mapper文件 --> <mapper resource="com/zhang/dao/UserDaoMapper.xml"/> </mappers> </configuration>
-
编写mybatis工具类
//sqlSessionFactory --->sqlSession public class MybatisUtils { 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 命令所需的所有方法 public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(); } }
3.3、编写代码
-
实体类
package com.zhang.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; /** * 和数据库中的字段做映射 */ @Data @NoArgsConstructor @AllArgsConstructor @ToString public class User { private int id; private String userName; private String pwd; private int gender; }
-
Dao接口
package com.zhang.dao; import com.zhang.pojo.User; import java.util.List; public interface UserDao { //查询所有用户 List<User> selectAllUsers(); }
-
接口实现类由原来的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 namespace="com.zhang.dao.UserDao"> <!--id = 所需重写的接口抽象方法名 resultType = 查询后所需返回的对象类型--> <select id="selectAllUsers" resultType="user"> select id, id, username, pwd, gender from mybatis.users </select> </mapper> <!-- 1.约束文件 "http://mybatis.org/dtd/mybatis-3-mapper.dtd" 约束文件作用:定义和限制当前文件中可以使用的标签和属性,以及标签出现的顺序 2.mapper是根标签 namespace:命名空间,必须有值,不能为空,唯一值 推荐使用Dao接口的全限定名称 3.在mapper标签中可以写<insert>、<delete>、<update>、<select>等标签 -->
3.4、测试
注:org.apache.ibatis.binding.BindingException: Type interface com.zhang.dao.UserDao is not known to the MapperRegistry.
解决方案:核心配置文件中注册mappers
-
junit测试
package com.zhang; import com.zhang.dao.UserDao; import com.zhang.pojo.User; import com.zhang.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class TestUserDao { @Test public void testSelectUserById() { //第一步:获得sqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //方式一:getMapper 推荐使用 UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> users = mapper.selectAllUsers(); for (User user : users) { System.out.println(user); } //方式二:需要转换 //List<User> userList = sqlSession.selectList("com.zhang.dao.UserDao.selectAllUsers"); //for (User user : userList) { // System.out.println(user); //} //关闭sqlSession sqlSession.close(); } }
注:Could not find resource com/zhang/dao/UserDaoMapper.xml
解决方案:由于maven约定大于配置,在pom.xml的文件中修改maven的默认规则
<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<!--更改maven编译规则-->
<resources>
<resource>
<!--资源目录-->
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
4、CRUD
4.1、select
4.1.1、序号参数绑定
public interface UserDao {
//根据用户id和姓名查询用户
User selectUserByIdAndUserName(Integer id, String userName); //原生参数绑定
}
<select id="selectUserByIdAndUserName" resultType="user">
select id, username, pwd, gender
from mybatis.users
where id = #{arg0}
and username = #{arg1} <!--arg0、arg1、arg2、argn-->
</select>
<select id="selectUserByIdAndUserName" resultType="user">
select id, username, pwd, gender
from mybatis.users
where id = #{param1}
and username = #{param2} <!--param1、param2、param3、param(n)-->
</select>
4.1.2、注解参数绑定(推荐)
import org.apache.ibatis.annotations.Param; //引入注解
public interface UserDao {
//根据用户id和密码查询用户
User selectUserByIdAndPassword(@Param("id") Integer id, @Param("password") String password); //使用Mybatis提供的@Param进行参数绑定
}
<select id="selectUserByIdAndPassword" resultType="user">
select id, username, pwd, gender
from mybatis.users
where id = #{id}
and pwd = #{password} <!--使用注解值@param{pwd}-->
</select>
4.1.3、Map参数绑定
public interface UserDao {
User selectUserByUserNameAndPassword(Map values); //添加Map进行参数绑定
}
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map values = new HashMap(); //测试类创建Map
values.put("myusername", "李思"); //自定义key,绑定参数
values.put("mypwd", "666666");
User user = mapper.selectUserByUserNameAndPassword(values);
<select id="selectUserByUserNameAndPassword" resultType="user">
select id, username, pwd, gender
from mybatis.users
where username = #{myusername}
and pwd = #{mypwd} <!--通过key获取value-->
</select>
4.1.4、对象参数绑定
public interface UserDao {
User selectUserByUserNameAndPassword1(User user); //使用对象进行参数绑定
}
<select id="selectUserByUserNameAndPassword1" resultType="user">
select id, username, pwd, gender
from mybatis.users
where username = #{userName}
and pwd = #{pwd} <!--#{userName}取User对象的userName属性值,#{pwd}同理-->
</select>
4.1.5、模糊查询
public interface UserDao {
List<User> selectByUserNameLike(@Param("keyword") String userName); //根据用户名模糊查询
}
<select id="selectByUserNameLike" resultType="user">
select id, username, pwd, gender
from mybatis.users
where username like concat('%',#{keyword},'%') <!--使用mysql concat拼接 '%'-->
</select>
4.2、delete
<delete id="deleteUserById" parameterType="int">
delete
from mybatis.users
where id = #{id}
</delete>
4.3、update
<update id="updateUserById" parameterType="user">
update mybatis.users
set username = #{userName},
pwd = #{pwd},
gender = #{gender}
where id = #{id} <!--方法参数为对象时,可直接使用#{属性名}获取-->
</update>
4.4、insert
<insert id="addUser" parameterType="user">
insert into mybatis.users
values (#{id}, #{userName}, #{pwd}, #{gender});
insert into mybatis.users
values (null, #{userName}, #{pwd}, #{gender}); <!--自动主键-->
</insert>
注:增、删、改需要提交事务!
4.5、主键回填
4.5.1、通过last-insert_id()查询主键
<insert id="addUser" parameterType="user">
<!--主键回填,将新数据的ID,存入java对象和主键对应的属性中-->
<selectKey order="AFTER" resultType="int" keyProperty="id"> <!--插入之后-->
select last_insert_id() <!--适用于整数类型自增主键-->
</selectKey>
insert into mybatis.users
values (#{id}, #{userName}, #{pwd}, #{gender});
</insert>
4.5.2、通过uuid()查询主键
<insert id="addStudent" parameterType="student">
<selectKey order="BEFORE" keyProperty="id" resultType="string"> <!--插入之前-->
select replace(uuid(),'-','') <!--适用于字符串类型主键-->
</selectKey>
insert into mybatis.student values (#{id},#{name},#{age})
</insert>
5、配置解析
5.1、核心配置文件
-
mybatis-config.xml
-
Mybatis配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
5.2、环境配置(environments)
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
Mybatis默认的事务管理器就是JDBC,连接池:POOLED
5.3、属性(properties)
可以通过properties属性来引用配置文件;
这些属性可以在外部配置且进行动态替换。既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【db.properties】
编写一个配置文件
db.properties
#key=value
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
username = root
password = root
在核心配置文件中映入
<!--引入web配置文件-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="pwd" value="root"/>
</properties>
- 可以直接引入外部文件
- 可以在其中增加一些属性配置
- 如果两个文件有同一个字段,优先使用外部配置文件
5.4、类型别名(typeAliases)
-
类型别名可为 Java 类型设置一个缩写名字
-
它仅用于 XML 配置,意在降低冗余的全限定类名书写
<!--可以给实体类起别名--> <typeAliases> <typeAlias type="com.zhang.pojo.Users" alias="user"/> </typeAliases>
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
扫描实体类的包,它的默认别名就为这个类的类名:首字母小写
<!--可以给实体类起别名--> <typeAliases> <package name="com.zhang.pojo"/> </typeAliases>
在实体类比较少的时候,使用第一种方式
如果实体类十分多,推荐使用第二种
第一种可以DIY(自定义)别名,第二种则不行(可以通过注解添加别名)
@Alias("user")
public class Users {
private int id;
//......
}
5.5、设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为;
5.6、其他配置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins
- mybatis-generator-core
- mybatis-plus
- 通用mapper
5.7、映射器(mappers)
MapperRegistry:注册绑定我们的mapper文件;
方式一:【推荐使用】
<!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!-->
<mappers>
<mapper resource="com/zhang/dao/UserMapper.xml"/>
</mappers>
方式二:使用class文件绑定注册
<mappers>
<mapper class="com.zhang.dao.UserMapper"/>
</mappers>
注:
- UserMapper接口要和它的UserMapper.xml配置文件必须同名!并且必须在同一包下!
方式三:使用扫描包进行注入绑定
<mappers>
<package name="com.zhang.dao"/>
</mappers>
注意点:使用同方式二一致!
6、日志
6.1、什么是日志?
用于记录系统中发生的各种事件。记录的位置常见的有:控制台、磁盘文件等
日志的级别
日志级别从低到高:
TRACE、DEBUG、 INFO、 WARN、 ERROR、FATAL
日志的作用
- 通过日志观察、分析项目的运行情况(项目维护)
- 通过日志分析用户的使用情况(大数据分析)
- ……
- SLF4J
- LOG4J
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING (控制台输出)
- NO_LOGGING
在Mybatis中具体使用那个日志实现,在设置中设定!
STDOUT_LOGGING标准日志输出
在mybatis核心配置文件中,配置日志!
<settings>
<!--标准的日志工厂-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
6.2、Log4j+Commons-Logging
什么是log4j?
- Log4j是
Apache
的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件 - 我们也可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
- 通过一个
配置文件
来灵活地进行配置,而不需要修改应用的代码。
-
导入依赖
项目中添加Log4j和Commons-Logging依赖
<dependencies> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> </dependencies>
-
基本使用
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Test; public class LogTest { //日志对象 private Log log = LogFactory.getLog(LogTest.class); //需要输出日志的类,可以创建一个log属性 @Test public void testLog(){ log.trace("hello trace~~"); log.debug("hello debug~~"); log.info("hello info~~"); log.warn("hello warn~~"); log.error("hello error~~"); log.fatal("hello fatal~~"); } }
-
配置信息
定义配置文件log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//log4j/log4j Configuration//EN" "log4j.dtd">
<log4j:configuration>
<!-- 日志输出到控制台 -->
<appender name="myConsole" class="org.apache.log4j.ConsoleAppender">
<!-- 日志输出格式 -->
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%p][%-d{yyyy-MM-dd HH:mm:ss SSS}][%c]-[%p] %m%n"/>
</layout>
<!--过滤器设置输出的级别-->
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<!-- 设置日志输出的最小级别 -->
<param name="levelMin" value="TRACE"/>
<!-- 设置日志输出的最大级别 -->
<param name="levelMax" value="FATAL"/>
</filter>
</appender>
<!-- 输出日志到文件 -->
<appender name="myFile" class="org.apache.log4j.FileAppender">
<!-- 输出文件全路径名-->
<param name="File" value="d:/log/fileAppender.log"/>
<!--是否在已存在的文件追加写:默认时true,若为false则每次启动都会删除并重新新建文件-->
<param name="Append" value="false"/>
<param name="Threshold" value="INFO"/>
<!--是否启用缓存,默认false-->
<param name="BufferedIO" value="false"/>
<!--缓存大小,依赖上一个参数(bufferedIO), 默认缓存大小8K -->
<param name="BufferSize" value="512"/>
<!-- 日志输出格式 -->
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%p][%d{yyyy-MM-dd HH:mm:ss SSS}][%c]-[%m]%n"/>
</layout>
</appender>
<!-- 输出日志到文件,当文件大小达到一定阈值时,自动备份 -->
<!-- FileAppender子类 -->
<appender name="rollingAppender" class="org.apache.log4j.RollingFileAppender">
<!-- 日志文件全路径名 -->
<param name="File" value="d:/log/rollingAppender.log"/> <!--文件位置-->
<!--是否在已存在的文件追加写:默认时true,若为false则每次启动都会删除并重新新建文件-->
<param name="Append" value="true"/>
<!-- 保存备份日志的最大个数,默认值是:1 -->
<param name="MaxBackupIndex" value="10"/>
<!-- 设置当日志文件达到此阈值的时候自动回滚,单位可以是KB,MB,GB,默认单位是KB,默认值是:10MB -->
<param name="MaxFileSize" value="10KB"/>
<!-- 设置日志输出的样式 -->`
<layout class="org.apache.log4j.PatternLayout">
<!-- 日志输出格式 -->
<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%-5p] [method:%l]%n%m%n%n"/>
</layout>
</appender>
<!-- 日志输出到文件,可以配置多久产生一个新的日志信息文件 -->
<appender name="dailyRollingAppender" class="org.apache.log4j.DailyRollingFileAppender">
<!-- 文件文件全路径名 -->
<param name="File" value="d:/log/dailyRollingAppender.log"/>
<!--是否追加-->
<param name="Append" value="true"/>
<!-- 设置日志备份频率,默认:为每天一个日志文件 -->
<param name="DatePattern" value="'.'yyyy-MM-dd'.log'"/>
<!--每分钟一个备份-->
<!--<param name="DatePattern" value="'.'yyyy-MM-dd-HH-mm'.log'" />-->
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%p][%d{HH:mm:ss SSS}][%c]-[%m]%n"/>
</layout>
</appender>
<!--
1. 指定logger的设置,additivity是否遵循缺省的继承机制
2. 当additivity="false"时,root中的配置就失灵了,不遵循缺省的继承机制
3. 代码中使用Logger.getLogger("logTest")获得此输出器,且不会使用根输出器
-->
<logger name="logTest" additivity="false">
<level value="INFO"/>
<appender-ref ref="dailyRollingAppender"/>
</logger>
<!-- 根logger的设置,若代码中未找到指定的logger,则会根据继承机制,使用根logger-->
<root>
<!--优先级设置:all < trace < debug < info < warn < error < fatal < off-->
<priority value="all"/>
<appender-ref ref="myConsole"/>
<appender-ref ref="fileAppender"/>
<appender-ref ref="rollingAppender"/>
<appender-ref ref="dailyRollingAppender"/>
</root>
</log4j:configuration>
7、Mybatis工具类
7.1、封装工具类
- Resource:用于获得读取配置文件的I0对象,耗费资源,建议通过10-次性读取所有所需要的数据。
- SlSessionFactory: SqlSession工 类,内存占用多,耗费资源,建议每个应用只创建一个对象。
- SqlSession:相当于Connection, 可控制事务,应为线程私有,不被多线程共享。
- 将获得连接、关闭连接、提交事务、回滚事务、获得接口实现类等方法进行封装。
package com.zhang.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;
/**
* 1、加载配置
* 2、构建SqlSessionFactory
* 3、创建SqlSession
* 4、事务的管理
* 5、Mapper的获取
*/
public class MybatisUtils {
//创建ThreadLocal绑定当前线程中的SqlSession对象
private static final ThreadLocal<SqlSession> t1 = new ThreadLocal<SqlSession>();
private static SqlSessionFactory sqlSessionFactory;
static { //加载配置信息,并构建sqlSessionFactory
try {
//1.加载配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//2.构建sqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//通过sqlSessionFactory创建SqlSession
//SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
public static SqlSession getSqlSession() {
//线程唯一,全局不唯一
SqlSession sqlSession = t1.get();
if (sqlSession == null) {
sqlSession = sqlSessionFactory.openSession();
t1.set(sqlSession);
}
return sqlSession;
}
//事务提交
public static void commit() {
SqlSession sqlSession = getSqlSession();
sqlSession.commit();
closeSqlSession();
}
//事务回滚
public static void rollback() {
SqlSession sqlSession = getSqlSession();
sqlSession.rollback();
closeSqlSession();
}
//资源释放
public static void closeSqlSession() {
SqlSession sqlSession = t1.get();
if (sqlSession != null) sqlSession.close();
}
//获取Mapper
public static <T> T getMapper(Class<T> mapper) {
SqlSession sqlSession = getSqlSession();
return sqlSession.getMapper(mapper);
}
}
编写测试类,可以看出,步骤简化很多了
package com.zhang;
import com.zhang.dao.StudentDao;
import com.zhang.pojo.Student;
import com.zhang.utils.MybatisUtils;
import org.junit.Test;
public class TestStudentDao {
@Test
public void testAddStudent() {
StudentDao mapper = MybatisUtils.getMapper(StudentDao.class);
Student s1 = new Student(null, "testUtil", "18");
mapper.addStudent(s1);
System.err.println(s1.getId());
MybatisUtils.commit();
MybatisUtils.closeSqlSession();
}
}
8、使用注解开发
8.1、面向接口编程
- 大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
- 根本原因:解耦 ,可拓展、提高复用、分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好
- 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;
- 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
关于接口的理解
- 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
- 接口的本身反映了系统设计人员对系统的抽象理解。
- 接口应有两类:
- 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
- 第二类是对一个个体某一方面的抽象, 即形成-一个抽象面(interface) ;
- 一个体有可能有多个抽象面。抽象体与抽象面是有区别的。
三个面向区别
- 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法
- 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
- 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程) 不是一个问题,更多的体现就是对系统整体的架构
8.2、使用注解开发
通过在接口中直接添加Mybatis注解,完成CRUD
@Select(" select id, username, pwd, gender from mybatis.users")
List<User> getusers();
需要在核心配置文件中绑定接口!
<mappers>
<!--<mapper resource="com/zhang/dao/UserDaoMapper.xml"/>-->
<mapper class="com.zhang.dao.UserDao"/> <!--class="接口全限定名"-->
</mappers>
注:接口注解定义完毕后,需将接口全限定名注册到mybatis-config.xml的< mappers >中
经验:注解模式属于硬编码到java文件中,失去了使用配置文件外部修改的优势,可结合需求选用。
本质:反射机制实现
底层:动态代理
8.2.1、查询
public interface UserDao {
@Select(" select id, username, pwd, gender from mybatis.users")
List<User> queryAllUser();
@Select("select id, username, pwd, gender from mybatis.users where id = #{id}")
User queryUserById(@Param("id") Integer id);
}
8.2.2、删除
public interface UserDao {
@Delete("delete from mybatis.users where id = #{id}")
Integer deleteUser(@Param("id") Integer id);
}
8.2.3、添加
public interface UserDao {
@Options(useGeneratedKeys = true,keyProperty = "id") //自增key,主键为id
@Insert(" insert into mybatis.users values (null, #{username}, #{pwd}, #{gender})")
Integer insertUser(User user);
}
8.2.4、修改
public interface UserDao {
@Update("update mybatis.users set username=#{username},pwd=#{pwd},gender=#{gender} where id=#{id}")
Integer updateUser(User user);
}
9、ORM映射
9.1、Mybatis自动ORM失效
MyBatis只能自动维护库表”列名“与”属性名“相同时的一-对应关系, 二者不同时,无法自动ORM。
9.2、方案一:列的别名
在SQL中使用as为查询字段添加列别名,以匹配属性名。
<mapper namespace="com.zhang.dao.PersonDao">
<select id="selectAllPerson" resultType="person">
select id, username as name, password as pwd, gender as sex
from mybatis.person
</select>
</mapper>
9.3、方案二:结果集映射(ResultMap-查询结果的封装规则)
通过映射,匹配列名与属性名
<mapper namespace="com.zhang.dao.PersonDao"> <!--使用resultMap标签定义更复杂的映射规则--> <resultMap id="person_resultMap" type="person"> <!--关联主键与列名--> <id column="id" property="id"></id> <!--关联列名与属性--> <result column="username" property="name"></result> <result column="password" property="pwd"></result> <result column="gender" property="sex"></result> </resultMap> <!--使用resultMap作为ORM映射--> <select id="selectAllPerson" resultMap="person_resultMap"> select id, username, password, gender from mybatis.person </select></mapper>
10、Mybatis处理关联关系-多表连接
实体间的关系:关联关系(拥有has、属于belong)
- OneToOne:一对一关系(Passenger(旅客)-- Passport(护照))
- OneToMany:一对多关系 (Employee(员工) 一Department(部门))
- ManyToMany:多对多关系(Student(学生) – Subject(科目))
10.1、OneToOne
环境搭建
create table t_passengers( `id` int primary key auto_increment, name varchar(50), sex varchar(1), birthday date)default charset = utf8;create table t_passports( id int auto_increment primary key , nationality varchar(50), expire date, passenger_id int unique , foreign key (passenger_id) references t_passengers(id))default charset = utf8;insert into t_passengers values (null,'张绍刚','男','2002-1-1');insert into t_passengers values (null,'李晓峰','男','2001-5-15');insert into t_passengers values (null,'林晓东','男','1999-6-15');insert into t_passports values (null,'中国','2035-1-1',1);insert into t_passports values (null,'美国','2050-1-1',2);insert into t_passports values (null,'英国','2040-1-1',3);
<mapper namespace="com.zhang.dao.PassengerDao"> <!--结果集映射(查询结果的封装规则)--> <resultMap id="passenger_passport" type="passenger"> <id column="id" property="id"></id> <result column="name" property="name"></result> <result column="sex" property="sex"></result> <result column="birthday" property="birthday"></result> <!--关系表中数据的封装规则--> <!--描述passport nationality expire-:映射规则--> <association property="passport" javaType="passport"> <!--指定关系表的实体属性--> <id column="passId" property="id"></id> <result column="nationality" property="nationality"></result> <result column="expire" property="expire"></result> </association> </resultMap> <!--查询旅客及其护照信息--> <!--多表连接查询--> <!--结果集映射(查询结果的封装规则)--> <select id="queryPassengerById" resultMap="passenger_passport"> <!--别名,避免id冲突--> select enger.id, name, sex, birthday, passenger_id passId, port.nationality, port.expire from t_passengers as enger inner join t_passports as port on enger.id = port.passenger_id where enger.id = #{id} </select></mapper>
10.2、OneToMany
环境搭建
create table t_departments( id int primary key auto_increment, name varchar(50), location varchar(100))default charset = utf8;create table t_employees( id int primary key auto_increment, name varchar(50), salary double, dept_id int, foreign key (dept_id) references t_departments(id))default charset = utf8;insert into t_departments values (null,'研发部','北京'),(null,'教学部','上海'),(null,'品牌部','西安');insert into t_employees values (null,'李晓明',10000,1),(null,'林小芳',6000,1),(null,'张立伟',8000,2),(null,'赵伟峰',8800,2),(null,'林晓鸥',12000,3),(null,'赵凯峰',11000,3)
<mapper namespace="com.zhang.dao.DepartmentDao"> <!--封装规则--> <resultMap id="dept_emp" type="department"> <id column="id" property="id"></id> <result column="name" property="name"></result> <result column="location" property="location"></result> <!--emp_id emp_name salary employee--> <!--关系表中数据的封装骨规则--> <!--指定关系表的实体类型--> <collection property="employee" ofType="Employee"> <id column="emp_id" property="id"></id> <result column="emp_name" property="name"></result> <result column="salary" property="salary"></result> </collection> </resultMap> <!--多表连接查询--> <!--封装规则--> <select id="queryDepartmentById" resultMap="dept_emp"> <!--别名,避免id冲突--> select d.id, d.name, location, emp.id emp_id, emp.name emp_name, emp.salary from t_departments d inner join t_employees emp on d.id = emp.dept_id where dept_id = #{id} </select></mapper>
10.3、ManyToMany
环境搭建
create table t_student( id int primary key auto_increment, name varchar(50), sex varchar(1)) default charset = utf8;create table t_subject( id int primary key auto_increment, name varchar(50), grade varchar(1)) default charset = utf8;create table t_stu_sub( student_id int, subject_id int, foreign key (student_id) references t_student (id), foreign key (subject_id) references t_subject (id), primary key (student_id, subject_id)) default charset = utf8;insert into t_student values (null,'小张','男'),(null,'小林','女');insert into t_subject values (1001,'JavaSE','1'),(1002,'JavaWeb','2');insert into t_stu_sub values (1,1001),(1,1002),(2,1001),(2,1002);
<mapper namespace="com.zhang.dao.SubjectDao"> <!--映射查询只封装两表中的信息,可忽略关系表内容--> <resultMap id="subject_student" type="subject"> <id column="sub_id" property="id"></id> <result column="sub_name" property="name"></result> <result column="grade" property="grade"></result> <collection property="studentList" ofType="student"> <id column="stu_id" property="id"></id> <result column="stu_name" property="name"></result> <result column="sex" property="sex"></result> </collection> </resultMap> <!--三表连接查询--> <!--封装规则--> <select id="querySubjectById" resultMap="subject_student"> select subject_id sub_id,sub.name sub_name,grade,t_student.id stu_id,t_student.name stu_name,sex from t_student inner join t_subject sub on t_student.id = sub.id <!--通过t_stu_sub建立两者之间的关系--> join t_stu_sub on t_student.id = t_stu_sub.student_id where subject_id = #{id} </select></mapper>
注:指定"多方"关系时(集合),使用
JavaType用来指定实体类中属性的类型
ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
10.4、关系总结
一方,添加对象;多方,添加集合。
双方均可建立关系属性,建立关系属性后,对应的Mapper文件中需使用< ResultMap >完成多表映射。
持有对象关系属性,使用< association property=“dept” javaType="department >
持有集合关系属性,使用< collction property=“emps” ofType="employee >
11、动态SQL
MyBatis的映射文件中支持在基础SQL上添加一些逻辑操作,并动态拼接成完整的SQL之后再执行,以达到SQL复用、简化编程的效果
11.1、 SQL
<mapper namespace="com.zhang.dao.UserDao"> <!--抽取通用的sql片段--> <sql id="user_field"> select id, username, pwd, gender from mybatis.users </sql> <select id="queryAllUser" resultType="user"> <!--通过ID引用sql片段--> <include refid="user_field"></include> </select></mapper>
11.2、where
<mapper namespace="com.zhang.dao.UserDao"> <select id="queryUsers" resultType="user"> <include refid="user_field"></include> <!--where标签: 1.补充where关键字 2.where字句中以or、and开头,会将or、and去掉 --> <where> <if test="username!=null">username=#{username}</if> <if test="gender!=null">and gender=#{gender}</if> </where> </select></mapper>
11.3、set
<mapper namespace="com.zhang.dao.UserDao"> <update id="updateUser" parameterType="user"> update mybatis.users <!-- set标签: 1.补充set 2.自动将set字句的,去除 --> <set> <if test="username!=null">username=#{username},</if> <if test="pwd!=pwd">pwd=#{pwd},</if> <if test="gender!=gender">gender=#{gender},</if> </set> where id = #{id} </update></mapper>
11.4、trim
代替where、set
<mapper namespace="com.zhang.dao.UserDao"> <select id="queryUsers" resultType="user"> <include refid="user_field"></include> <!-- prefix="where" 补充where关键字 prefixOverrides where字句以or或and开头,会被去除掉 --> <trim prefix="where" prefixOverrides="or|and"> <!--添加where前缀,自动忽略前缀--> <if test="username!=null">username=#{username}</if> <if test="gender!=null">and gender=#{gender}</if> </trim> </select></mapper>
<mapper namespace="com.zhang.dao.UserDao"> update mybatis.users <!-- prefix="set" 补充set关键字 suffixOverrides 自动将set字句的,去除 --> <trim prefix="set" suffixOverrides=","> <!--添加set前缀,自动忽略后缀--> <if test="username!=null">username=#{username},</if> <if test="pwd!=pwd">pwd=#{pwd},</if> <if test="gender!=gender">gender=#{gender},</if> </trim> where id = #{id} </update></mapper>
11.5、foreach
<mapper namespace="com.zhang.dao.UserDao"> <!--批量删除--> <delete id="deleteManyUser" parameterType="java.util.List"> <!--delete from mybatis.users where id in(x,x,x,x)--> delete from mybatis.users where id in <foreach collection="list" open="(" close=")" item="id6" separator=","> #{id6} </foreach> </delete> <!--批量添加--> <insert id="insertManyUser" parameterType="java.util.List"> <!--insert into mybatis.users values (null,x,x,x),(null,x,x,x)--> insert into mybatis.users values <foreach collection="list" item="user6" separator=","> (null,#{user6.username},#{user6.pwd},#{user6.gender}) </foreach> </insert></mapper>
参数 | 描述 | 取值 |
---|---|---|
collection | 容器类型 | list、array、map |
open | 起始符 | ( |
close | 结束符 | ) |
separator | 分隔符 | , |
index | 下标号 | 从0开始,依次递减 |
item | 当前项 | 任意名称(循环中通过#{任意名称}表达式访问) |
12、缓存(Cache)
内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问。
12.1、一级缓存
SqlSession级别的缓存,同一个SqlSession的发起多次同构查询, 会将数据保存在一级缓存中。
注:无需任何配置,默认开启一级缓存
12.2、二级缓存
SqlSessionFactory级别的缓存, 同一个SqlSessionFactory构建的SqlSession发起的多次同构查询,会将数据保存在二级缓存中
注:在Sqlsession.commit()或者Sqlsession.close()之后生效
12.2.1、开启全局缓存
setting是Mybatis中极为重要的调整位置,他们会改变Mybatis的运行行为,其他详细配置可参考官方文档
<configuration> <!--导入连接外部参数--> <properties resource="db.properties"/> <!--设置--> <settings> <!--注意书写位置--> <setting name="cacheEnabled" value="true"/> <!--mybatis-config.xml中开启全局缓存(默认开启)--> </settings> <typeAliases> <package name="com.zhang.pojo"/> </typeAliases></configuration>
12.2.2、指定Mapper缓存
<mapper namespace="com.zhang.dao.UserDao"> <!--二级缓存是默认开启的,但并不是所有的查询结果,都会进入二级缓存(相当于邀请函)--> <cache/></mapper>
12.2.3、缓存清空并重新缓存
@Testpublic void testMapperCache(){ //通过相同的sqlSessionFactory获取多个SqlSession SqlSession sqlSession1 = MybatisUtils.openSqlSession(); UserDao mapper1 = sqlSession1.getMapper(UserDao.class); List<User> users1 = mapper1.queryAllUser(); sqlSession1.close(); //必须关闭sqlSession才可缓存数据 //修改 修改相关的数据,会被移除 SqlSession sqlSession4 = MybatisUtils.openSqlSession(); UserDao mapper4 = sqlSession4.getMapper(UserDao.class); Integer i = mapper4.deleteUser(5010); sqlSession4.commit(); //DML成功,数据发生变化,缓存清空 sqlSession4.close(); SqlSession sqlSession2 = MybatisUtils.openSqlSession(); UserDao mapper2 = sqlSession2.getMapper(UserDao.class); System.out.println("***************"); List<User> users2 = mapper2.queryAllUser(); sqlSession2.close(); //缓存未击中,重新查询数据库、重新缓存 System.out.println("-----------------"); SqlSession sqlSession3 = MybatisUtils.openSqlSession(); UserDao mapper3 = sqlSession3.getMapper(UserDao.class); List<User> users3 = mapper3.queryAllUser(); sqlSession2.close();}
13、Druid连接池
13.1、什么是Druid连接池?
Druid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和SQL解析器组成。该项目主要是为了扩展JDBC的一些限制,可以让程序员实现一些特殊的需求,比如向密钥服务请求凭证、统计SQL 信息、SQL 性能收集、SQL 注入检查、SQL 翻译等,程序员可以通过定制来实现自己需要的功能。
13.2、不同连接池对比
测试执行申请归还连接1000000(一百万)次总耗时性能对比
13.1.1、测试环境
环境 | 版本 |
---|---|
OS | OS X 10.8.2 |
CPU | Intel i7 2GHz 4 Core |
JVM | Java Version 1.7.0_05 |
13.1.2、基准测试结果对比
- Druid是性能最好的数据库连接池,tomcat-jdbc和druid性能接近。
- Proxool在激烈并发时会抛异常,不适用。
- C3P0和Prool都相当慢,影响sql执行效率。
- BoneCP性能并不优越,采用LinkedTransterQueue并没有能够获得性能提升。
- 除了bonecp, 其他的在JDK7. 上跑得比JDK6上快。
- jboss datasource虽然稳定,但性能很糟糕。
13.3、配置pom.xml
在pom.xml引入Druid依赖
<!--druid连接池--><!-- https://mvnrepository.com/artifact/com.alibaba/druid --><dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.22</version></dependency>
13.4、创建DruidDataSourceFactory
MyDruidDataSourceFactory并继承PooledDataSourceFactory,并替换数据源。
package com.zhang.utils;import com.alibaba.druid.pool.DruidDataSource;import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;/** * 连接池工厂 */public class MyDruidDataSourceFactory extends PooledDataSourceFactory { public MyDruidDataSourceFactory() { this.dataSource = new DruidDataSource(); //替换数据源 }}
13.5、修改mybatis-config.xml
mybatis-config.xml中连接池相关配置
<!--连接池--><dataSource type="com.zhang.utils.MyDruidDataSourceFactory"> <!--数据源工厂--> <!--数据连接参数--> <property name="driverClass" value="${driver}"/> <property name="jdbcUrl" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/></dataSource>
注:< property name=“属性名” />属性名必须com.alibaba.druid.pool.DruidAbstractDataSource中一致。
14、PageHelper
14.1、什么是PageHelper?
PageHelper是适用于MyBatis框架的一个分页插件,使用方式极为便捷,支持任何复杂的单表、多表分页查询操作。
14.2、访问与下载
下载地址:download
14.3、开发步骤
PageHelper中提供了多个分页操作的静态方法入口。
14.3.1、引入依赖
pom.xml中引入PageHelper依赖
<!--pageHelper分页--><!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper --><dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.2.0</version></dependency>
14.3.2、配置mybatis-config.xml
在mybatis-config.xml中添加
<plugins> <!--com.github.pagehelper为pagehelper类所在包名--> <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin></plugins>
14.3.3、PageHelper应用方式
使用PageHelper提供的静态方法设置分页查询条件
//在查询前,设置分页,查询第一页,每页5条数据//PageHelper 对其之后的第一个功能,进行分页功能追加PageHelper.startPage(1,5);
14.4、PageInfo对象
14.4.1、PageInfo应用方式
使用PageInfo保存分页查询结果
@Testpublic void testPage(){ //在查询前,设置分页,查询第一页,每页5条数据 //PageHelper 对其之后的第一个功能,进行分页功能追加 PageHelper.startPage(1,5); List<User> users = mapper.queryAllUser(); for (User user : users) { System.out.println(user); } //将查询结果封装到pageInfo对象中 PageInfo<User> pageInfo = new PageInfo<User>(users); System.out.println(pageInfo); MybatisUtils.closeSqlSession();}
14.4.2、注意事项
- 只有在PagetHelperstartPage0方法之后的第一个查询会有执行分页。
- 分页插件**不支持带有"for update"**的查询语句。
- 分页插件不支持**“嵌套查询**",会导致结果集折叠。所以无法保证分页结果数量正确。。
public class PageInfo<T> extends PageSerializable<T> {
//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//当前页面第一个元素在数据库中的行号
private long startRow;
//当前页面最后一个元素在数据库中的行号
private long endRow;
//总页数
private int pages;
//上一页
private int prePage;
//下一页
private int nextPage;
//是否是第一页
private boolean isFirstPage;
//是否是最后一页
private boolean isLastPage;
//是否有上一页
private boolean hasPreviousPage;
//是否有下一页
private boolean hasNextPage;
private int navigatePages;
private int[] navigatepageNums;
private int navigateFirstPage;
private int navigateLastPage;
}
15、$符号的应用场景
${attribute}属于字符串拼接SQL,而非预编译占位符,会有注入攻击问题,不建议在常规SQL中使用,常用于可解决动态升降序问题
15.1、$符号参数绑定
public interface UserDao {
List<User> queryUsersByUserName(@Param("userName") String userName);
List<User> queryUsersByIdDesc(@Param("rule")String rule); //desc asc
}
<mapper namespace="com.zhang.dao.UserDao">
<select id="queryUsersByUserName" resultType="user">
select id, username, pwd, gender
from mybatis.users
where username = '${userName}' <!--拼接userName,如果字符串类型需要用单引号:'${userName}'-->
</select>
<select id="queryUsersByIdDesc" resultType="user">
select id, username, pwd, gender
from mybatis.users
order by id ${rule} <!--拼接desc | asc-->
</select>
</mapper>
public class TestUserDao {
@Test
public void testQueryUsersByIdDesc() {
UserDao mapper = MybatisUtils.getMapper(UserDao.class);
List<User> users = mapper.queryUsersByIdDesc("desc"); //调用时传入desc | asc
}
}
15.2、$符号注入攻击
<mapper namespace="com.zhang.dao.UserDao">
<select id="queryUsersByUserName" resultType="user">
select id, username, pwd, gender
from mybatis.users
where username = '${userName}'
</select> <!--会存在注入攻击,比如传入参数是【String name = "xxx' or '1'='1"】-->
</mapper>
/**
* #{}:占位符 可以规避sql注入风险
* 原则:填充数据,要和列相关的位置才能使用
* select * from users where id = ?
* insert into users values (?,?,?)
* update users set username = ?,pwd = ?
* ${}:存在sql注入问题,随意拼接数据
*/
@Test
public void testInject() {
UserDao mapper = MybatisUtils.getMapper(UserDao.class);
List<User> users = mapper.queryUsersByUserName("xxx' or '1'='1"); //由于存在sql注入问题,导致查询结果把全部用户给显示出来
for (User user : users) {
System.out.println(user);
}
}