Mybatis:
ORM:Object Relationa Mapping:即对象关系映射,ORM是一种规范,它是是将数据库的记录与实体类对象进行一一映射。
MyBatis官网地址(中文版):https://mybatis.org/mybatis-3/zh/index.html
MyBatis的功能架构:
MyBatis执行流程:
1.mybatis基本使用
web开发的三层架构 (mvc )
三层架构
界面层: 和用户打交道的, 接收用户的请求参数, 显示处理结果的。(jsp ,html ,servlet)
业务逻辑层: 接收了界面层传递的数据,计算逻辑,调用数据库,获取数据
数据访问层: 就是访问数据库, 执行对数据的查询,修改,删除等等的。
三层对应的包
界面层: controller包 (servlet)
业务逻辑层: service 包(XXXService类)
数据访问层: dao包(XXXDao类)
三层中类的交互
用户使用界面层–> 业务逻辑层—>数据访问层(持久层)–>数据库(mysql)
三层对应的处理框架
界面层—servlet—springmvc(框架)
业务逻辑层—service类–spring(框架)
数据访问层—dao类–mybatis(框架)
使用 JDBC 的缺陷
代码比较多,开发效率低
需要关注 Connection ,Statement, ResultSet 对象创建和销毁
对 ResultSet 查询的结果,需要自己封装为 List
重复的代码比较多些
业务代码和数据库的操作混在一起
MyBatis 解决的主要问题
mybatis完全是一个升级版的JDBC,他是可以完全独立抽取出来的使用的.
MyBatis 可以完成:
注册数据库的驱动,
创建 JDBC 中必须使用的 Connection , Statement, ResultSet 对象
从 xml 中获取 sql,并执行 sql 语句,把 ResultSet 结果转换 java 对象
4.关闭资源
ResultSet.close() , Statement.close() , Conenection.close()
mybatis使用步骤:
1.导入maven环境
2.创建dao接口,里面定了你需要对数据库执行什么操作
3.编写mapper文件,sql映射文件,写上你的sql语句
4.创建mybatis主配置文件,读取配置文件中的内容,连接到数据库
5.使用mybatis对象执行sql语句:sqlSession对象
6.关闭连接:sqlSession.close();
1.创建数据库表:
CREATE TABLE t_user (
id int NOT NULL AUTO_INCREMENT,
username varchar(255) DEFAULT NULL,
password varchar(255) DEFAULT NULL,
email varchar(255) DEFAULT NULL,
phone varchar(255) DEFAULT NULL,
PRIMARY KEY (id)
)
2.创建实体类与表中的数据进行对应:
public class User {
private Integer id;
private String username;
private String password;
private String email;
private String phone;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public User() {
}
public User(Integer id, String username, String password, String email, String phone) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.phone = phone;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
3.导入对应的maven依赖:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
</dependencies>
<build>
<!-- 把mybatis配置文件拷贝到target/clasess目录下-->
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
4.1编写dao层接口:
public interface UserManagerDao {
/**
* 查询所有的数据 用于视图展示
* @return 返回一个List集合
*/
public List<User> queryUser();
/**
* 添加
* @param user 一个user对象
* @return 成功返回true 失败返回false
*/
public Boolean addUser(User user);
/**
* 按照用户名查找用户
* @param name 需要查找的用户名
* @return 返回一个List集合
*/
public List<User> likeUser(String name);
}
4.2编写mapper.xml文件
<?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 namespace="bookMall.dao.UserManagerDao">
<!-- id对应的是dao接口中的方法名 -->
<select id="queryUser" resultType="bookMall.bean.User" >
<!--查询操作-->
select id,username,password,email,phone from t_user where id>0
</select>
<select id="likeUser" resultType="bookMall.bean.User" >
<!--跟参数的查询操作-->
select id,username,password,email,phone from t_user where username=#{username}
</select>
<!--添加操作-->
<insert id="addUser" >
insert into t_user values(#{id},#{username},#{username},#{email},#{phone})
</insert>
</mapper>
5.配置mybatis.xml配置文件:
<?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>
<!--环境配置: 数据库的连接信息 default:必须和某个environment的id值一样。 告诉mybatis使用哪个数据库的连接信息。也就是访问哪个数据库 -->
<environments default="mydev">
<!-- environment : 一个数据库信息的配置, 环境 id:一个唯一值,自定义,表示环境的名称。 -->
<environment id="mydev">
<!--transactionManager :mybatis的事务类型 type: JDBC(表示使用jdbc中的Connection对象的commit,rollback做事务处理) -->
<transactionManager type="JDBC"/>
<!-- dataSource:表示数据源,连接数据库的 type:表示数据源的类型, POOLED表示使用连接池 -->
<dataSource type="POOLED">
<!-- driver, user, username, password 是固定的,不能自定义。 -->
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://localhost/book_ctiy?serverTimezone=GMT%2B8"/>
<!--访问数据库的用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="2732195202"/>
</dataSource>
</environment>
</environments>
<!-- sql mapper(sql映射文件)的位置-->
<mappers>
<!--一个mapper标签指定一个文件的位置。 从类路径开始的路径信息。 target/clasess(类路径)-->
<mapper resource="bookMall\dao\UserManagerDao.xml"/>
</mappers>
</configuration>
6.测试查询用于接口:
@Test
void queryUser() throws IOException {
//访问mybatis读取student数据
//1.定义mybatis主配置文件的名称, 从类路径的根开始(target/clasess)
String config="mybatis.xml";
//2.读取这个config表示的文件
InputStream in = Resources.getResourceAsStream(config);
//3.创建了SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//4.创建SqlSessionFactory对象
SqlSessionFactory factory = builder.build(in);
//5.获取SqlSession对象,从SqlSessionFactory中获取SqlSession
SqlSession sqlSession = factory.openSession();
//6.【重要】指定要执行的sql语句的标识。 sql映射文件中的namespace + "." + 标签的id值
String sqlId = "bookMall.dao.UserManagerDao" + "." + "queryUser";
//String sqlId = "bookMall.dao.UserManagerDao";
//7. 重要】执行sql语句,通过sqlId找到语句
List<User> studentList = sqlSession.selectList(sqlId);
//8.输出结果
//studentList.forEach( stu -> System.out.println(stu));
for(User user : studentList){
System.out.println("查询的学生="+user);
}
//9.关闭SqlSession对象
sqlSession.close();
}
注意事项:
1.target目录下一定要有这两个文件,如果没有,手动复制进去也行
2.mysql的依赖配置如果是高版本的:
driver的值是:com.mysql.cj.jdbc.Driver
3.mysql的依赖配置如果是高版本的,url是这样的:
dbc:mysql://localhost/book_ctiy?serverTimezone=GMT%2B8
一定要加上时区,不然会报错
4.在pom.xml配置文件中需要加上,才能更好的将pom.xml和UserManagerDao.xml文件映射出去
<build>
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
2.工具类封装和实现增删改查
1.MyBatis工具类:
public class MyBatisUtils {
private static SqlSessionFactory factory = null;
static {
//读取mybatis.xml配置文件
String config="mybatis.xml";
try {
//将配置文件加入到流中
InputStream in = Resources.getResourceAsStream(config);
//创建factory对象
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
SqlSession sqlSession = null;
if (factory!=null){
//如果 factory!=null 就创建 sqlSession对象
sqlSession = factory.openSession(false);//非自动提交事务
}
return sqlSession;
}
}
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>
<!-- 配置全局属性 -->
<settings>
<!-- 使用jdbc的getGeneratedKeys获取数据库自增主键值 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 使用列别名替换列名 默认:true -->
<setting name="useColumnLabel" value="true" />
<!-- 开启驼峰命名转换:Table{create_time} -> Entity{createTime} -->
<setting name="mapUnderscoreToCamelCase" value="true" />
<!-- 输出mybatis 执行sql 语句的日志信息-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<environments default="localhost">
<!-- environment : 一个数据库信息的配置, 环境 id:一个唯一值,自定义,表示环境的名称。 -->
<environment id="localhost">
<!--transactionManager :mybatis的事务类型 type: JDBC(表示使用jdbc中的Connection对象的commit,rollback做事务处理) -->
<transactionManager type="JDBC"/>
<!-- dataSource:表示数据源,连接数据库的 type:表示数据源的类型, POOLED表示使用连接池 -->
<dataSource type="POOLED">
<!-- driver, user, username, password 是固定的,不能自定义。 -->
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://localhost/book_ctiy?serverTimezone=GMT%2B8"/>
<!--访问数据库的用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="2732195202"/>
</dataSource>
</environment>
</environments>
<!--设置别名-->
<typeAliases>
<!--name:实体类所在的包名(不是实体类的包名也可以)-->
<package name="com.books.bean"/>
</typeAliases>
<!-- sql mapper(sql映射文件)的位置-->
<mappers>
<!--
name:是包名, 这个包中的所有mapper.xml一次都能加载
使用package的要求:
1. mapper文件名称和dao接口名必须完全一样,包括大小写
2. mapper文件和dao接口必须在同一目录
-->
<package name="com.books.dao"/>
</mappers>
</configuration>
3.1dao接口
public interface UserManagerDao {
/**
* 查询所有的数据 用于视图展示
* @return 返回一个List集合
*/
public List<User> queryUser();
/**
* 添加
* @param user 一个user对象
* @return 成功返回true 失败返回false
*/
public Boolean addUser(User user);
/**
* 删除用户
* @param id 需要删除的用户id
* @return 成功返回true 失败返回false
*/
public Boolean delUser(String id);
/**
* 修改用户信息
* @param user 需要一个user对象
* @return 成功返回true 失败返回false
*/
public Boolean updateUser(User user);
/**
* 按照用户名查找用户
* @param name 需要查找的用户名
* @return 返回一个List集合
*/
public List<User> likeUser(String name);
}
3.2mapper.xml文件
<?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">
<mapper namespace="com.mybatis.dao.UserManagerDao">
<select id="queryUser" resultType="com.mybatis.bean.User">
select id,username,password,email,phone from t_user
</select>
<select id="likeUser" parameterType="String" resultType="com.mybatis.bean.User">
select * from t_user where username like '%${username}%'
</select>
<insert id="addUser">
insert into t_user values(#{id},#{username},#{username},#{email},#{phone})
</insert>
<delete id="delUser">
delete from t_user where id=#{id}
</delete>
<update id="updateUser">
update t_user set username=#{username},password=#{password},email=#{email},phone=#{phone} where id=#{id}
</update>
</mapper>
4.编写service层
public class UserManagerDaoImpl implements UserManagerDao {
/**
* 查询所有的数据 用于视图展示
*
* @return 返回一个List集合
*/
@Override
public List<User> queryUser() {
//获取sqlSession对象
SqlSession sqlSession = MyBatisUtils.getSqlSession();
String sqlId = "com.mybatis.dao.UserManagerDao.queryUser";
//执行sql语句
List<User> userList = sqlSession.selectList(sqlId);
// sqlSession.commit(); //mybatis默认不会手动提交事务,在修改数据之后要 提交事务
//关闭连接
sqlSession.close();
return userList;
}
/**
* 添加
*
* @param user 一个user对象
* @return 成功返回true 失败返回false
*/
@Override
public Boolean addUser(User user) {
//获取sqlSession对象
SqlSession sqlSession = MyBatisUtils.getSqlSession();
String sqlId = "com.mybatis.dao.UserManagerDao.addUser";
//执行sql语句
int updateCount = sqlSession.update(sqlId,user);
sqlSession.commit(); //mybatis默认不会手动提交事务,在修改数据之后要 提交事务
//关闭连接
sqlSession.close();
if(updateCount>0){
return true;
}else{
return false;
}
}
/**
* 删除用户
*
* @param id 需要删除的用户id
* @return 成功返回true 失败返回false
*/
@Override
public Boolean delUser(String id) {
//获取sqlSession对象
SqlSession sqlSession = MyBatisUtils.getSqlSession();
String sqlId = "com.mybatis.dao.UserManagerDao.delUser";
//执行sql语句
int delete = sqlSession.delete(sqlId, id);
sqlSession.commit(); //设置为自动提交事务
//关闭连接
sqlSession.close();
if(delete>0){
return true;
}else {
return false;
}
}
/**
* 修改用户信息
*
* @param user 需要一个user对象
* @return 成功返回true 失败返回false
*/
@Override
public Boolean updateUser(User user) {
//获取sqlSession对象
SqlSession sqlSession = MyBatisUtils.getSqlSession();
String sqlId = "com.mybatis.dao.UserManagerDao.updateUser";
//执行sql语句
int updateCount = sqlSession.update(sqlId,user);
sqlSession.commit(); //设置为自动提交事务
//关闭连接
sqlSession.close();
if(updateCount>0){
return true;
}else {
return false;
}
}
/**
* 按照用户名查找用户
*
* @param name 需要查找的用户名
* @return 返回一个List集合
*/
@Override
public List<User> likeUser(String name) {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
String sqlId = "com.mybatis.dao.UserManagerDao.likeUser";
List<User> userList = sqlSession.selectList(sqlId, name);
sqlSession.commit();
//关闭连接
sqlSession.close();
return userList;
}
}
5.测试所写的方法:
class UserManagerDaoImplTest {
UserManagerDaoImpl userManagerDao = new UserManagerDaoImpl();
@Test
void queryUser() {
List<User> userList = userManagerDao.queryUser();
for (User user : userList) {
System.out.println(user);
}
}
@Test
void ListUser() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserManagerDao userDao = sqlSession.getMapper(UserManagerDao.class);
List<User> userList = userDao.queryUser();
for (User user : userList) {
System.out.println(user);
}
}
@Test
void addUser() {
User user = new User(null,"admin","root","root@qq.com","1008644");
Boolean flag = userManagerDao.addUser(user);
System.out.println(flag);
}
@Test
void delUser() {
Boolean flag = userManagerDao.delUser("23");
System.out.println(flag);
}
@Test
void updateUser() {
User user = new User(19,"admin2","root2","root@qq.com2","1008644");
Boolean flag = userManagerDao.updateUser(user);
System.out.println(flag);
}
@Test
void likeUser() {
List<User> userList = userManagerDao.likeUser("admin");
for (User user : userList) {
System.out.println(user);
}
}
}
3.mybatis中主要类的介绍
-
Resources: mybatis中的一个类, 负责读取主配置文件
InputStream in = Resources.getResourceAsStream(“mybatis.xml”); -
SqlSessionFactoryBuilder : 创建SqlSessionFactory对象,
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//创建SqlSessionFactory对象
SqlSessionFactory factory = builder.build(in); -
SqlSessionFactory : 重量级对象, 程序创建一个对象耗时比较长,使用资源比较多。
SqlSessionFactory:接口 , 接口实现类: DefaultSqlSessionFactory
SqlSessionFactory作用: 获取SqlSession对象。
SqlSession sqlSession = factory.openSession();
openSession()方法说明:
-
openSession() :无参数的, 获取是非自动提交事务的SqlSession对象
-
openSession(boolean): openSession(true) 获取自动提交事务的SqlSession.
openSession(false) 非自动提交事务的SqlSession对象 -
SqlSession: 执行sql语句的对象
SqlSession接口 :定义了操作数据的方法 例如 selectOne() ,selectList() ,insert(),update(), delete(), commit(), rollback()
SqlSession接口的实现类DefaultSqlSession。
使用要求: SqlSession对象不是线程安全的,需要在方法内部使用, 在执行sql语句之前,使用openSession()获取SqlSession对象。
在执行完sql语句后,需要关闭它,执行SqlSession.close(). 这样能保证他的使用是线程安全的
4.nybatis实现动态代理
5.1mybatis参数传递
- 使用@Param进行传参:
1.接口中的方法
2.mapper中的SQL
- 使用java对象进行传参
-
使用map的key值进行传参:
接口层:
Mapper文件:
测试:
5.2Mybatis配置文件
<?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="jdbc.properties"/>
<settings>
<!--输出mybatis 执行sql 语句的日志信息-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 开启驼峰命名 -->
<setting name="useColumnLabel" value="true" />
</settings>
<!-- 定义别名1:-->
<typeAliases> <!-- 可以指定一个类型的别名:type:自定义的全限定名称-->
<typeAlias type="com.mybatis.bean.User" alias="User"></typeAlias>
<!-- 定义别名的方式2:扫描bean包下的所有实体类 -->
<!-- <package name="com.mybatis.bean"/> -->
</typeAliases>
<!-- 指定使用哪个数据源-->
<environments default="localhost">
<!-- environment : 一个数据库信息的配置, 环境 id:一个唯一值,自定义,表示环境的名称。 -->
<environment id="localhost">
<!--transactionManager :mybatis的事务类型 type: JDBC(表示使用jdbc中的Connection对象的commit,rollback做事务处理) -->
<transactionManager type="JDBC"/>
<!-- dataSource:表示数据源,连接数据库的 type:表示数据源的类型, POOLED表示使用连接池 -->
<dataSource type="POOLED">
<!-- driver, user, username, password 是固定的,不能自定义。 -->
<!--数据库的驱动类名-->
<property name="driver" value="${jdbc.driver}"/>
<!--连接数据库的url字符串-->
<property name="url" value="${jdbc.url}"/>
<!--访问数据库的用户名-->
<property name="username" value="${jdbc.user}"/>
<!--密码-->
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="onLine">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://121.196.44.70/book_ctiy??serverTimezone=GMT%2B8"/>
<!--访问数据库的用户名-->
<property name="username" value="book_ctiy"/>
<!--密码-->
<property name="password" value="admin123?"/>
</dataSource>
</environment>
</environments>
<!-- sql mapper(sql映射文件)的位置-->
<mappers>
<!-- 一个mapper标签指定一个文件的位置。 从类路径开始的路径信息。 target/clasess(类路径) -->
<mapper resource="com\mybatis\dao\UserManagerDao.xml"/>
<!-- <package name="com.mybatis.dao"/>-->
<!-- mapper文件所在的包名,这个包中的所有文件都将会一次性加载给mybatis
要求: 1.mapper文件名称和接口文件名一样,区分大小写
2.mapper文件名称和接口文件需要在同一目录 -->
</mappers>
</configuration>
1.properties标签
为外部配置数据库连接文件properties指定默认值:MyBatis 3.4.2 开始
<dataSource type="POOLED">
<!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
<property name="username" value="${username:ut_user}"/>
</dataSource>
这个特性默认是关闭的。要启用这个特性,需要添加一个特定的属性来开启这个特性。
<properties resource="org/mybatis/example/config.properties">
<!-- 启用默认值特性 -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
</properties>
2.设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 |
true | false | false |
aggressiveLazyLoading | 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods )。 |
true | false | false (在 3.4.1 及之前的版本中默认为 true) |
multipleResultSetsEnabled | 是否允许单个语句返回多结果集(需要数据库驱动支持)。 | true | false | true |
useColumnLabel | 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 | true | false | true |
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 | true | false | False |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
autoMappingUnknownColumnBehavior | 指定发现自动映射目标未知列(或未知属性类型)的行为。 NONE : 不做任何反应 WARNING : 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN ) FAILING : 映射失败 (抛出 SqlSessionException ) |
NONE, WARNING, FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 | 任意正整数 | 未设置 (null) |
defaultFetchSize | 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 | 任意正整数 | 未设置 (null) |
defaultResultSetType | 指定语句默认的滚动策略。(新增于 3.5.2) | FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置) | 未设置 (null) |
safeRowBoundsEnabled | 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 | true | false | False |
safeResultHandlerEnabled | 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。 | true | false | True |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 | SESSION | STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 | OTHER |
lazyLoadTriggerMethods | 指定对象的哪些方法触发一次延迟加载。 | 用逗号分隔的方法列表。 | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定动态 SQL 生成使用的默认脚本语言。 | 一个类型别名或全限定类名。 | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
defaultEnumTypeHandler | 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5) |
一个类型别名或全限定类名。 | org.apache.ibatis.type.EnumTypeHandler |
callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 | true | false | false |
returnInstanceForEmptyRow | 当返回行的所有列都是空时,MyBatis默认返回 null 。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2) |
true | false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | 任何字符串 | 未设置 |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
proxyFactory | 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 | CGLIB | JAVASSIST | JAVASSIST (MyBatis 3.3 以上) |
vfsImpl | 指定 VFS 的实现 | 自定义 VFS 的实现的类全限定名,以逗号分隔。 | 未设置 |
useActualParamName | 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) |
true | false | true |
configurationFactory | 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) |
一个类型别名或完全限定类名。 | 未设置 |
shrinkWhitespacesInSql | 从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5) | true | false | false |
defaultSqlProviderType | Specifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type (or value ) attribute on sql provider annotation(e.g. @SelectProvider ), when these attribute was omitted. |
A type alias or fully qualified class name | Not set |
一个完整的配置如下:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
3.类型别名(typeAliases)
3.1.类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
3.2.也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliase
每一个在包 domain.blog
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author
的别名为: author
;若有注解,则别名为其注解
@Alias("myAuthor")
public class Author {
...
}
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
4.插件:
<!--配置分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
5.环境配置(environments)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema (模式)的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例.
每个数据库对应一个 SqlSessionFactory 实例
<!-- 指定使用哪个数据源-->
<environments default="localhost">
<!-- environment : 一个数据库信息的配置, 环境 id:一个唯一值,自定义,表示环境的名称。 -->
<environment id="localhost">
<!--transactionManager :mybatis的事务类型 type: JDBC(表示使用jdbc中的Connection对象的commit,rollback做事务处理) -->
<transactionManager type="JDBC"/>
<!-- dataSource:表示数据源,连接数据库的 type:表示数据源的类型, POOLED表示使用连接池 -->
<dataSource type="POOLED">
<!-- driver, user, username, password 是固定的,不能自定义。 -->
<!--数据库的驱动类名-->
<property name="driver" value="${jdbc.driver:com.mysql.cj.jdbc.Driver}"/>
<!--连接数据库的url字符串-->
<property name="url" value="${jdbc.url:dbc:mysql://localhost/myData?serverTimezone=GMT%2B8}"/>
<!--访问数据库的用户名-->
<property name="username" value="${jdbc.user:root}"/>
<!--密码-->
<property name="password" value="${jdbc.password:2732195202}"/>
</dataSource>
</environment>
<environment id="onLine">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://131.196.12.10/myData?serverTimezone=GMT%2B8"/>
<!--访问数据库的用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="admin"/>
</dataSource>
</environment>
</environments>
6.事务管理器(transactionManager)
7.映射器(mappers)
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:///
形式的 URL),或类名和包名等。例如:
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
8.数据库厂商标识(databaseIdProvider)
MyBatis会加载带有匹配当前数据库databaseId属性和所有不带databaseId属性的语句。如果同时找到带有databaseId和不带databaseId的相同语句,则后者会被舍弃。
# 需要在environmentsb标签下机芯配置
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
<property name="MySQL" value="mysql" />
</databaseIdProvider>
# 在写sql时,可以通过databaseId进行指定的数据库进行解析sql
<select id="selectOrder" resultMap="orderResultMap" databaseId="mysql">
select order_id, create_time, price, status, user_id from t_order;
</select>
9.数据源配置:
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/myData?serverTimezone=GMT%2B8"/>
<property name="username" value="postgres"/>
<property name="password" value="root"/>
</dataSource>
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
-
poolMaximumActiveConnections` – 在任意时间可存在的活动(正在使用)连接数量,默认值:10
-
poolMaximumIdleConnections
– 任意时间可能存在的空闲连接数。 -
poolMaximumCheckoutTime
– 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒) -
poolTimeToWait
– 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。 -
poolMaximumLocalBadConnectionTolerance
– 这是一个关于坏连接容忍度的底层设置,作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过poolMaximumIdleConnections
与poolMaximumLocalBadConnectionTolerance
之和。 默认值:3(新增于 3.4.5) -
poolPingQuery
– 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。 -
poolPingEnabled
– 是否启用侦测查询。若开启,需要设置poolPingQuery
属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。 -
poolPingConnectionsNotUsedFor
– 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。
9.selects属性配置
属性 | 描述 |
---|---|
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType |
将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap | 用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 |
resultType |
期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
resultMap |
对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
flushCache |
将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。 |
useCache |
将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
fetchSize |
这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。 |
statementType |
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
resultSetType |
FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。 |
databaseId |
如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |
resultOrdered |
这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false 。 |
resultSets |
这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。 |
10.insert, update 和 delete
属性 | 描述 |
---|---|
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType |
将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap |
用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 |
flushCache |
将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType |
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys |
仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。 |
keyProperty |
(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset )。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn |
(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
databaseId |
如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |
11.selectKey 元素的属性
属性 | 描述 |
---|---|
keyProperty |
selectKey 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn |
返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
resultType |
结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。 |
order |
可以设置为 BEFORE 或 AFTER 。如果设置为 BEFORE ,那么它首先会生成主键,设置 keyProperty 再执行插入语句。如果设置为 AFTER ,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。 |
statementType |
和前面一样,MyBatis 支持 STATEMENT ,PREPARED 和 CALLABLE 类型的映射语句,分别代表 Statement , PreparedStatement 和 CallableStatement 类型。 |
6.$和#的区别(重点掌握)
select id,name, email,age from student where id=#{studentId}
(#) 的结果: select id,name, email,age from student where id=?
select id,name, email,age from student where id=${studentId}
( $) 的结果:select id,name, email,age from student where id=1001
String sql=“select id,name, email,age from student where id=” + “1001”;
使用的Statement对象执行sql, 效率比PreparedStatement低。
$:可以替换表名或者列名, 你能确定数据是安全的。可以使用$
#和 $ 区别
1. #使用 ?在sql语句中做站位的, 使用PreparedStatement执行sql,效率高
2. #能够避免sql注入,更安全。
3. $不使用占位符,是字符串连接方式,使用Statement对象执行sql,效率低
4. $有sql注入的风险,缺乏安全性。
5. $:可以替换表名或者列名
7.ResultType的使用
parameterType:接收参数的类型
resultType:返回结果映射的类型
mybatis返回值类型支持的别名机制对应表
resultType:的返回值可以是别名,也可以是返回值类型的全限定义名称(可以是任意类型)
自定义 resultType:的返回值类型
自定义别名第一种方式:
1.在mybatis主配置文件中加上typeAliases标签
2.在mapper.xml文件中使用即可
自定义别名第二种方式:
1.在mybatis主配置文件中加上typeAliases标签
2.在mapper.xml文件中使用即可
8.动态SQL
1.if标签
/**
* 动态sql的使用 在使用动态sql 的时候 需要使用java对象作为参数
* @param user 传入一个user对象
* @return
*/
public List <User> selectUserIf( User user);
# if test中可以使用 and 或者 or 运算符
<select id="selectUserIf" resultType="User">
select id,username,password,email,phone from t_user where
<if test="username != null and username !='' ">
username = #{username}
</if>
<if test="id >0 ">
and id>#{id}
</if>
</select>
2.if和where标签一起使用:
/**
* 动态sql的使用 在使用动态sql 的时候 需要使用java对象作为参数
* @param user 传入一个user对象
* @return
*/
public List <User> selectUserWhere( User user);
<!-- <where> 用来包含 多个<if>的, 当多个if有一个成立的, <where>会自动增加一个where关键字,
并去掉 if中多余的 and ,or等... -->
# 如果if中的条件不成立则查询全部的数据
<select id="selectUserWhere" resultType="com.mybatis.bean.User">
select id,username,password,email,phone from t_user
<where>
<if test="username !=null and username !='' ">
username = #{username}
</if>
<if test="id >0 ">
or id>#{id}
</if>
</where>
</select>
3.choose、when、otherwise
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句
/**
* 动态sql switch 根据传递过来的参数进行查询
* @param user
* @return
*/
public List<User> selectUserBySwitch(User user);
# 如果username不为空按照username模糊查询,如果username为空,phone不为空按照phone模糊查询,如果phone为空查询id=1的数据
<select id="selectUserBySwitch" resultType="User">
select id, username, password, email, phone from t_user where id>0
<choose>
<when test="username != null and username!='' ">
and username like #{username}
</when>
<when test=" phone != null and phone != null">
and phone like #{phone}
</when>
<otherwise>
and id=1
</otherwise>
</choose>
</select>
// 动态sql choose、when、otherwise
@Test
void selectUserBySwitch() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserManagerDao mapper = sqlSession.getMapper(UserManagerDao.class);
User user = new User();
user.setUsername("a%");
List<User> userList = mapper.selectUserBySwitch(user);
userList.forEach(System.out::println);
}
模糊查询:%代表匹配多个字符,_代表匹配一个字符
4.trim、where、set
前面几个例子已经方便地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “state = ‘ACTIVE’” 设置成动态条件,看看会发生什么。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:
SELECT * FROM BLOG
WHERE
这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:
SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’
这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。
MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
# 等价的 trime 标签
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</trim>
5.set动态修改,如果满足条件就修改改列,不满足则不修改
/**
* 动态sql: 动态修改数据 满足条件修改该字段,不满足则不修改
* @param user
* @return
*/
public Integer updateByUserId(User user);
<!-- 动态修改数据 -->
<update id="updateByUserId">
update t_user
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="phone != null">phone=#{phone}</if>
</set>
where id=#{id}
</update>
// 动态sql 动态修改数据
@Test
void insertByUserId() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserManagerDao mapper = sqlSession.getMapper(UserManagerDao.class);
User user = new User();
user.setId(3);
user.setUsername("compass");
user.setPassword("compass123");
user.setEmail("compass@qq.com");
Integer userId = mapper.updateByUserId(user);
; sqlSession.commit();
sqlSession.close();
System.out.println(userId);
}
注意:增删改的时候需要手动提交事务,否则会进行回滚,想要自动提交事务,可以在配置文件中进行配置。
6.Foreach 标签的使用:
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
/**
* 动态sql的使用 在使用动态sql 的时候 需要使用java对象作为参数
* @param idList 传入一个int List集合
* @return
*/
public List <User> selectUserForeachInteger(List<Integer> idList);
/**
* 动态sql的使用 在使用动态sql 的时候 需要使用java对象作为参数
* @param studentList 传入一个int List集合
* @return
*/
public List <User> selectUserForeachUser(List<User> studentList);
<!--
Foreach List<Integer>
collection="接口中参数的数据类型,如果是数组使用array,如果是集合,是List"
item="自定义的,表示数组和集合的成员变量"
open="循环开始时候开始字符"
close="循环的时候的结束的字符"
separator="集合成员之间的分隔符"
index:表示索引 如果是map集合句表示的是key -->
<select id="selectUserForeachInteger" resultType="com.mybatis.bean.User">
select id,username,password,email,phone from t_user where id in
<foreach collection="list" item="myId" open="(" close=")" separator="," index="index">
#{
myId}
</foreach>
</select>
<!-- Foreach List<User>
collection="接口中参数的数据类型,如果是数组使用array,如果是集合,是List"
item="自定义的,表示数组和集合的成员变量"
open="循环开始时候开始字符"
close="循环的时候的结束的字符"
separator="集合成员之间的分隔符"
#{
user.id}:循环对象的某个属性值
-->
<select id="selectUserForeachUser" resultType="com.mybatis.bean.User">
select id,username,password,email,phone from t_user where id in
<foreach collection="list" item="user" open="(" close=")" separator="," index="index">
#{
user.id}
</foreach>
</select>
// 传入int类型集合
@Test
void selectUserForeachInteger() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserManagerDao mapper = sqlSession.getMapper(UserManagerDao.class);
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
List<User> userList = mapper.selectUserForeachInteger(list);
userList.forEach(System.out::println);
}
// 传递userList集合
@Test
void selectUserForeachUser() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserManagerDao mapper = sqlSession.getMapper(UserManagerDao.class);
List<User> userList = new ArrayList<User>();
User userOne = new User();
userOne.setId(1);
userList.add(userOne);
User userTwo = new User();
userTwo.setId(2);
userList.add(userTwo);
List<User> userLists = mapper.selectUserForeachUser(userList);
userLists.forEach(System.out::println);
}
最终底层执行的sql是where in();操作
select id,username,password,email,phone from t_user where id in ( ? , ? )
7.bind标签 元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如:
/**
* 绑定参数的形式 进行模糊查询
* @return
*/
public List<User> selectUserByBind(User user);
<!-- bind标签绑定外部属性完成模糊查询 -->
<select id="selectUserByBind" resultType="User">
<bind name="tempName" value=" '%'+ username + '%' "/>
select id, username, password, email, phone from t_user where username like #{
tempName}
</select>
// 动态sql: bind标签绑定外部属性
@Test
void selectUserByBind() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserManagerDao mapper = sqlSession.getMapper(UserManagerDao.class);
User user = new User();
user.setUsername("a");
List<User> userList = mapper.selectUserByBind(user);
userList.forEach(System.out::println);
}
使用bind的好处,可以有效预防sql注入,也可以在数据库层就完成模糊查询,不需要在service层传递参数时拼接匹配符
9.SQL语句重复使用
<!-- 自定义sql 语句片段 -->
<sql id="userSql" >
select id,username,password,email,phone from t_user where id in
</sql>
<select id="sqlRepeat" resultType="com.mybatis.bean.User">
# 引入上面定义好的sql
<include refid="userSql"></include>
<foreach collection="list" item="user" open="(" close=")" separator=",">
#{user.id}
</foreach>
</select>
10.mybatis配置properties
1.使用properties标签引入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="jdbc.properties"/>
<environments default="localhost">
<environment id="localhost">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库的驱动类名-->
<property name="driver" value="${jdbc.driver}"/>
<!--连接数据库的url字符串-->
<property name="url" value="${jdbc.url}"/>
<!--访问数据库的用户名-->
<property name="username" value="${jdbc.user}"/>
<!--密码-->
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<!-- sql mapper(sql映射文件)的位置-->
<mappers>
<mapper resource="com\mybatis\dao\UserManagerDao.xml"/>
</mappers>
</configuration>
2.目录结构:jdb.properties和mybatis.xml配置文件必须都在resources目录下,而且jdbc.properties必须以properties后缀名结尾
3.jdbc.properties文件的书写格式:
4.mapper文件映射的两种方式:
11.mybatis分页查询
1.引入分页插件
<!-- 分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version> 5.1.10</version>
</dependency>
2.在mybatis配置文件中配置分页插件,在environments标签之前进行配置
SQL: select id,username,password,email,phone from t_user
//分页查询
@Test
void selectAll() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserManagerDao mapper = sqlSession.getMapper(UserManagerDao.class);
// pageNum:从第几页开始 pageSize:一页中有多少条数据
PageHelper.startPage(0,3);
List<User> userList = mapper.selectAll();
userList.forEach(System.out::println);
// PageInfo对象获取到分页属性属性
PageInfo<User> pageInfo = new PageInfo<User>(userList);
System.out.println("获取最后一行的id="+pageInfo.getEndRow());
}
PageInfo类中的属性:
获取到某一页的数据:(想看的那一页-1)* 每页显示的数量 ,如每页显示3条数据,我想查询看第5页的数据就是:(5-1)*3=12
limit 12,3;最终查看到了第五页的数据,左边的偏移量是需要变化的,右边的偏移量就是每页显示的条数
//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//由于startRow和endRow不常用,这里说个具体的用法
//可以在页面中"显示startRow到endRow 共size条数据"
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总页数
private int pages;
//前一页
private int prePage;
//下一页
private int nextPage;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
//导航条上的第一页
private int navigateFirstPage;
//导航条上的最后一页
private int navigateLastPage;
12.Mybatis关联映射
需要用到的四个表:用户表,游戏区表,订单表,中间表
# 用户表
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`password` varchar(32) NOT NULL,
`email` varchar(200) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `username` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
# 订单表
REATE TABLE `t_order` (
`order_id` varchar(50) NOT NULL,
`create_time` datetime DEFAULT NULL,
`price` decimal(11,2) DEFAULT NULL,
`status` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
PRIMARY KEY (`order_id`) USING BTREE,
KEY `user_id` (`user_id`) USING BTREE,
CONSTRAINT `t_order_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
# 游戏区表
CREATE TABLE `t_gamezone` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`game_zone` varchar(10) NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
# 多对多关系映射的 中间表
CREATE TABLE `t_user_gamezone` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`u_id` int(11) DEFAULT NULL,
`g_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `u_id` (`u_id`),
KEY `gamezone` (`g_id`),
CONSTRAINT `gamezone` FOREIGN KEY (`g_id`) REFERENCES `t_gamezone` (`id`),
CONSTRAINT `user` FOREIGN KEY (`u_id`) REFERENCES `t_user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
关系型数据对应的三种关系:一对一,一对多,多对多
1.Mybatis的popj对象和数据库字段名不一致,导致映射失败的解决方案:
1.在写sql的时候,使用别名机制,将别名取名为pojo的属性名
2.使用ResultMap定义结果集
<mapper namespace="com.mybatis.dao.OrderManagerDao">
<select id="selectOrder" resultMap="orderResultMap">
select order_id, create_time, price, status, user_id from t_order;
</select>
<!-- 1.使用别名机制 处理数据库字段和实体类不一致的解决方案 -->
<!-- 2.使用在查询是给字段名取别名,别名为实体类的属性名称 处理数据库字段和实体类不一致的解决方案 -->
<resultMap id="orderResultMap" type="Order">
<!-- id:主键 (实体类中属性) orderId(数据库字段名) -->
<id property="orderId" column="order_id" />
<!-- property:主键 (实体类中属性) column(数据库字段名) -->
<result property="createTime" column="create_time"/>
<result property="price" column="price"/>
<result property="status" column="status"/>
<result property="userId" column="user_id"/>
</resultMap>
</mapper>
2.一对一关系映射:
mapper.xml代码:
需要注意的是:select标签中的属性是resultMap=“userResultMap” 而不是 resultType=“userResultMap”
实现方式1:
<!--根据用户id查询订单详细信息 一对一关系映射查询-->
<select id="selectUserByOrderId" resultMap="userResultMap" parameterType="int">
select o.* ,u.*from t_user u left join t_order o on u.id=o.user_id where u.id=#{userId};
</select>
<!--自定义结果集查询-->
<resultMap id="userResultMap" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="password" column="password"></result>
<result property="email" column="email"></result>
<result property="phone" column="phone"></result>
<result property="order.orderId" column="order_id"></result>
<result property="order.createTime" column="create_time"></result>
<result property="order.price" column="price"></result>
<result property="order.status" column="status"></result>
<result property="order.userId" column="user_id"></result>
</resultMap>
实现方式2:
<!--根据用户id查询订单详细信息 一对一关系映射查询-->
<select id="selectUserByOrderId" resultMap="userResultMap" parameterType="int">
select o.* ,u.*from t_user u left join t_order o on u.id=o.user_id where u.id=#{userId};
</select>
<resultMap id="userResultMap" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="password" column="password"></result>
<result property="email" column="email"></result>
<result property="phone" column="phone"></result>
<!-- 此处定义的是User对象中包含的order对象的结果封装 -->
<association property="order" javaType="Order">
<id property="orderId" column="order_id"></id>
<result property="createTime" column="create_time"></result>
<result property="price" column="price"></result>
<result property="status" column="status"></result>
<result property="userId" column="user_id"></result>
</association>
</resultMap>
3.一对多的关系映射
mapper.xml代码:
<!--一对多关系映射 通过用户id查询出该用户的所有订单信息-->
<select id="selectUserByOrderIdList" resultMap="userResultMap2" parameterType="int">
select o.* ,u.*from t_user u left join t_order o on u.id=o.user_id where u.id=#{userId};
</select>
<resultMap id="userResultMap2" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="password" column="password"></result>
<result property="email" column="email"></result>
<result property="phone" column="phone"></result>
<!-- 此处定义的是User对象中包含的order对象的结果封装
orderList:User类中对应的list集合属性
ofType:当前属性集合对应的结果集
-->
<collection property="orderList" ofType="Order">
<id property="orderId" column="order_id"></id>
<result property="createTime" column="create_time"></result>
<result property="price" column="price"></result>
<result property="status" column="status"></result>
<result property="userId" column="user_id"></result>
</collection>
</resultMap>
4.多对多映射关系:
#查询出每个用户对应的游戏区
select u.*,g.* ,m.* from t_user u left join t_user_gamezone m on u.id=m.g_id join t_gamezone g on m.g_id=g.id ;
# 查询黑色玫瑰这个区下有哪些人?
select * from (select g.* from t_gamezone g where id=2 ) g join
t_user_gamezone m on g.id=m.g_id join t_user u on u.id=m.u_id;
# 查询admin用户到底选了哪些游戏区?
select g.*,u.* from (select * from t_user ) u join
t_user_gamezone m on u.id=m.u_id join t_gamezone g on g.id=m.g_id where u.id=2;
- 多对多关系 查询某个用户选了那些游戏区
<!-- 多对多关系 查询某个用户选了那些游戏区 selectByGameZoneId -->
<select id="selectByGameZoneId" resultMap="userResultMap3" parameterType="int">
select g.id gid,g.game_zone,u.* from (select * from t_user ) u join t_user_gamezone m on u.id=m.u_id join t_gamezone g on g.id=m.g_id where u.id=#{userId};
</select>
<resultMap id="userResultMap3" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="password" column="password"></result>
<result property="email" column="email"></result>
<result property="phone" column="phone"></result>
<!-- 此处定义的是User对象中包含的order对象的结果封装
orderList:User类中对应的list集合属性
ofType:当前属性集合对应的结果集
-->
<collection property="gameZoneList" ofType="GameZone">
<id property="id" column="gid"></id>
<result property="gameZone" column="game_zone"></result>
</collection>
</resultMap>
- 多对多关系,查看某个区下有哪些用户
<select id="findByGameZoneId" resultMap="resultMapGameZone" parameterType="int">
select u.id,username,password,email,phone,g.id as gid ,game_zone from (select * from t_gamezone where id=#{gameZoneId} ) g join t_user_gamezone m on g.id=m.g_id join t_user u on u.id=m.u_id;
</select>
<!-- 多对多关系,查看某个区下有哪些用户 -->
<resultMap id="resultMapGameZone" type="GameZone">
<!-- id:主键 (实体类中属性) orderId(数据库字段名) -->
<id property="id" column="gid" />
<!-- property:主键 (实体类中属性) column(数据库字段名) -->
<result property="gameZone" column="game_zone"/>
<collection property="userList" ofType="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="password" column="password"></result>
<result property="email" column="email"></result>
<result property="phone" column="phone"></result>
</collection>
</resultMap>
Spring:
spring官网地址(英文):https://spring.io/projects/spring-framework/
1.spring概述
Spring 的主要作用就是为代码“解耦”,降低代码间的耦合度。根据功能的不同,可以将一个系统中的代码分为主业务逻辑与系统级业务逻辑两类。它们各自具有鲜明的特点:主业务代码间逻辑联系紧密,有具体的专业业务应用场景,复用性相对较低;系统级业务相对功能独立,没有具体的专业业务应用场景,主要是为主业务提供系统级服务,如日志、安全、事务等,复用性强。
Spring 根据代码的功能特点,将降低耦合度的方式分为了两类:IoC 与 AOP。IOC 使得主
业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由 Spring 容器统一管理,自动“注入”。而 AOP 使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由 Spring 容器统一完成“织入”。Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。简单来说,Spring是一个分层的 Java SE/EE full-stack(一站式)轻量级开源框架。
---- 百度百科《Spring》
- Spring体系结构
pring 有两个核心部分:IOC 和 Aop
(1)IOC:控制反转,把创建对象过程交给 Spring 进行管理
(2)Aop:面向切面,不修改源代码进行功能增强
Spring 特点
(1)方便解耦,简化开发
(2)Aop 编程支持
(3)方便程序测试
(4)方便和其他框架进行整合
(5)方便进行事务操作
(6)降低 API 开发难度
2.ioc bean管理(xml方式实现)
1.pom.xml依赖
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- spring 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- 测试依赖-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
</dependencies>
2.实体类
public class User {
public void doMethods(){
System.out.println("doMethods方法被调用了");
}
}
3.applicationContext.xml配置文件:
<!-- 配置user类的对象创建
class 需要创建的对象的类路径
id: context.getBean("user"); 给对象起一个标识 用于创建对象的时候用
-->
<bean id="user" class="com.maven.spring.bean.User"> </bean>
4.ioc原理解析
-
底层用的技术: xml解析 ,反射 ,工厂模式
-
普通创建对象的方式和工厂类创建对象的模式
1、IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂
2、Spring 提供 IOC 容器实现两种方式:(两个接口)
(1)BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用
加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
(2)ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用.
加载配置文件时候就会把在配置文件对象进行创建
BeanFactory 接口容器:
BeanFactory 接口对象也可作为 Spring 容器出现。BeanFactory 接口是 ApplicationContext
接口的父类
而 Spring 配置文件以资源 Resouce 的形式出现在 XmlBeanFactory 类的构造器参数中。
Resouce 是一个接口,其具有两个实现类:
ClassPathResource:指定类路径下的资源文件
FileSystemResource:指定项目根路径或本地磁盘路径下的资源文件
创建了 BeanFactory 容器后,便可使用其重载的 getBean()方法,从容器中获取指定的 Bean 对象。在创建对象的时候 也是默认的去执行无参构造方法,默认自带无参构造方法,如果声明了有参构造方法,如果不做特别声明,无参构造方法就会消失.导致创建对象失败。
1.通过配置文件的方式用set方法给对象注入属性
通过set方法给bean对象注入属性:
spring配置文件:
<bean id="book" class="com.maven.spring.bean.Book">
<property name="name" value="java入门"></property>
<property name="author" value="卡夫卡"></property>
</bean>
Order实体类需要提供set方法,否则注入失败:
public class Order {
private String orderId;
private String price;
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public void setPrice(String price) {
this.price = price;
}
}
从容器中获得对象:
// 基于xml通过set方法给对象赋值
@Test
void doMethodsXmlSet() {
//加载spring配置文件的时候创建就对象
BeanFactory context = new ClassPathXmlApplicationContext("bean.xml");
Order order = (Order)context.getBean("order");
System.out.println(order);
}
2.通过构造方法给bean对象注入属性:
spring.xml文件
index:表示参数的位置,0表示构造方法的第一个参数
<bean id="book" class="com.maven.spring.bean.Book">
<!-- 使用有参构造方法给对象属性赋值 -->
<constructor-arg index="0" value="java入门"/>
<constructor-arg index="1" value="卡夫卡"/>
</bean>
name:对应的是构造方法中的属性 推荐使用,这种方式比较灵活
<bean id="book" class="com.maven.spring.bean.Book">
<!-- 使用有参构造方法给对象属性赋值 -->
<constructor-arg name="name" value="java入门"/>
<constructor-arg name="author" value="卡夫卡"/>
</bean>
Book实体类:实体类中的构造方法中的参数必须和配置文件中的一致,否则会导致注入属性失败
public class Book {
private String name;
private String author;
public Book(String name,String author) {
this.name = name;
this.author = author;
}
}
注入特殊属性:
1.null值
<bean id="book" class="com.maven.spring.bean.Book">
<!-- 使用有参构造方法给对象属性赋值 -->
<constructor-arg name="name" value="java入门"/>
注入null值的方式1:
<constructor-arg name="author" value="null"/>
注入null值的方式2:
<constructor-arg name="author" > <null/> </constructor-arg >
</bean>
2.注入特殊符号值:
<!-- 2 把带特殊符号内容写到 <![CDATA[特殊符号值]]> 中 -->
<property name="name">
<value> <![CDATA[<<南京>>]]> </value>
</property>
3.p名称空间注入
4.注入外部属性bean
将UserDaoImpl这个类的的对象注入到UserService中进行使用
public class UserService {
//创建UserDao类型属性 生成get方法 UserDaoImpl的UserDao实现类
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
<!--service 和 dao对象创建 外部bean -->
<bean id="userService" class="com.maven.spring.service.UserService">
<!-- 注入UserDao对象
name:类里面找属性
ref:UserDaoImpl对象的标签id值-->
<property name="userDao" ref="UserDaoImpl"> </property>
</bean>
<!-- 创建UserDaoImpl对象-->
<bean id="UserDaoImpl" class="com.maven.spring.dao.impl.UserDaoImpl> </bean>
<!--级联 bean-->
<bean id="emp" class="com.maven.spring.bean.Emp" > <!--创建Emp对象-->
<property name="name" value="张三"> </property> <!--给Emp对象属性赋值-->
<property name="sex" value="男"> </property>
<!--设置对象类型属性-->
<property name="dept" ref="dept"> <!-- 创建Dep对象 --></property>
<property name="dept.name" value="技术部"> <!-- 创建Dep对象 --></property>
</bean>
<bean id="dept" class="com.maven.spring.bean.Dept" > </bean>
5.内部bean的实现方式:
<!--内部 bean-->
<bean id="emp" class="com.maven.spring.bean.Emp" > <!--创建Emp对象-->
<property name="name" value="张三"> </property> <!--给Emp对象属性赋值-->
<property name="sex" value="男"> </property>
<!-- 设置对象类型属性-->
<property name="dept" > <!-- 创建Dep对象 -->
<bean id="dept" class="com.maven.spring.bean.Dept" >
<property name="name" value="安全部"> </property> <!-- 给Dep对象赋值 -->
</bean>
</property>
</bean>
6.注入集合类型属性:
实体类:
# 注:需要生成set方法
public class Student {
//数组属性类型
private String courses [] ;
//list集合
private List<String> students;
//map集合
private Map<String,String> map;
//set集合
private Set sets;
}
spring.xml配置文件
<bean id="student" class="com.maven.spring.bean.Student">
<!-- 注入数组类型 -->
<property name="courses">
<array>
<value>java课程</value>
<value>大数据</value>
</array>
</property>
<!-- 注入List集合类型 -->
<property name="students" >
<list>
<value>linux</value>
<value>运维工程师</value>
</list>
</property>
<!-- 注入Map集合类型-->
<property name="map">
<map>
<entry key="xiaoming" value="男"></entry>
<entry key="zhangsan" value="女"></entry>
</map>
</property>
<!-- 注入Set集合类型-->
<property name="sets" >
<set>
<value>javaSe</value>
<value>javaEE</value>
</set>
</property>
</bean>
List集合中注入对象:
public class Student {
// 集合中是对象属性
private List<Course> courseList;
spring.xml配置文件
<!-- 注入List集合类型 值的类型是对象-->
<property name="courseList">
<list>
<!-- 将course对象注入到list集合中-->
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
<!-- 创建course对象-->
<bean id="course1" class="com.maven.spring.bean.Course">
<property name="course" value="spring"></property>
</bean>
<!-- 创建course对象-->
<bean id="course2" class="com.maven.spring.bean.Course">
<property name="course" value="mybatis"></property>
</bean>
使用uitl命令空间标签完成List集合提取注入:
IOC 操作 Bean 管理(bean 作用域):
- 在 Spring.xml配置文件里面,设置创建 bean 实例是单实例还是多实例
- 在 Spring.xml配置文件里面,默认情况下,bean 是单实例对象
- bean 标签里面有属性(scope)用于设置单实例还是多实例
- scope 属性值
- 第一个值 默认值,singleton,表示是单实例对象
- 第二个值 prototype,表示是多实例对象
- singleton 和 prototype 区别
- singleton 单实例,prototype 多实例
- 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象
设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建 对象,在调用
getBean 方法时候创建多实例对象
<!-- 设置为多实例 -->
<bean id="book" class="com.maven.spring.bean.Book" scope="prototype"> </bean>
IOC 操作 Bean 管理(bean 生命周期)
1、生命周期 : 从对象创建到对象销毁的过程
2、bean 生命周期
(1)通过构造器创建 bean 实例(无参数构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
(3)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(4)bean 可以使用了(对象获取到了)
(5)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
bean类:
public class Orders {
private String name;
public void setName(String name) {
System.out.println("2:掉用set方法,赋值");
this.name = name;
}
public Orders() {
System.out.println("1:执行无参构造方法,创建对象");
}
//创建执行的初始化方法
public void initMethod(){
System.out.println("3:创建执行的初始化方法");
}
public void destroyMethod(){
System.out.println("5:销毁创建出来的bean对象");
}
}
spring.xml配置文件:
<bean id="orders" class="com.maven.spring.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="电脑"> </property>
</bean>
测试方法:需要手动关闭容器:
//bean生命周期
@Test
void lifeCycleMethods() {
//加载spring配置文件的时候创建就对象
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
Orders order = context.getBean("orders", Orders.class);
System.out.println("6:获取创建出来的bean实例对象");
//手动销毁创建出来的实例对象
context.close();
}
bean 的后置处理器,bean 生命周期有七步
(1)通过构造器创建 bean 实例(无参数构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
(3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
(6)bean 可以使用了(对象获取到了)
(7)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
public class Orders {
private String name;
public void setName(String name) {
System.out.println("2:掉用set方法,赋值");
this.name = name;
}
public Orders() {
System.out.println("1:执行无参构造方法,创建对象");
}
//创建执行的初始化方法
public void initMethod(){
System.out.println("4:创建执行的初始化方法");
}
public void destroyMethod(){
System.out.println("7:销毁创建出来的bean对象");
}
}
// 实现 BeanPostProcessor 接口 用于在spring.xml中配置后置管理器
------------------------分割线-----------------------------------
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("3:在我们初始化之前执行的方法");
return null;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("5:在我们初始化之后执行的方法");
return null;
}
}
spring.xml配置文件:
<bean id="orders" class="com.maven.spring.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="电脑"> </property>
</bean>
<!-- 配置后置处理器 -->
<bean id="myBeanPost" class="com.maven.spring.bean.MyBeanPost"></bean>
XML的方式完成自动装配:
设置元素的autowire属性就可以设定bean的自动装配模式。自动装配有5种模式。
注意:自动装配功能和手动装配要是同时使用,那么自动装配就不起作用。
public class Emp {
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
}
<!-- 实现自动装配
byName :根据属性名称进行注入 注入bean的id值和属性名称要一致
byType:根据属性类型进行自动注入
-->
<bean id="emp" class="com.maven.spring.autowire.Emp" autowire="byName">
# 原本使用的是外部ben可以完成注入,现在采用自动注入
<!-- <property name="dept" ref="dept"> </property>-->
</bean>
<bean id="dept" class="com.maven.spring.autowire.Dept"> </bean>
外部文件属性配置,文件名后置必须为properties结尾,方向resource目录下
# 连接数据库的驱动名称
jdbc.driver=com.mysql.cj.jdbc.Driver
#连接数据库的URL
jdbc.url=jdbc:mysql://localhost/book?serverTimezone=GMT%2B8
#用户名
jdbc.username=root
#密码
jdbc.password=admin
spring.xml配置文件:
<!-- 引入外部文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}" ></property>
<property name="url" value="${jdbc.url}" ></property>
<property name="username" value="${jdbc.username}" ></property>
<property name="password" value="${jdbc.password}" ></property>
</bean>
3.ioc bean管理(注解方式实现)
Spring 针对 Bean 管理中创建对象提供注解
(1)@Component 都可以
(2)@Service service层
(3)@Controller web层
(4)@Repository dao层
上面四个注解功能是一样的,都可以用来创建 bean 实例,只是在不同地方使用对应的注解可以增加代码的可读性
1.开启组件扫描:
<context:component-scan base-package="com.maven.spring.dao,com.maven.spring.service" />
- 如果扫描多个包,多个包使用逗号隔开
- 在类上面添加创建对象注解
- 在注解里面 value 属性值可以省略不写, 默认值是类名称,首字母小写 UserService – userService
// 作用 把UserService对象放入到spring容器中,并且取名叫UserService
@Component(value = "userService")
public class UserService {
public void add(){
System.out.println(" 成功创建UserService的实例对象");
}
}
开启配置扫描的细节做法:
<!-- 开启组件扫描 扫描com.maven.spring包中所有的类-->
<!-- 示例1: use-default-filters="false":表示不使用默认的filter(此包下的类全部进行扫描) 使用我们自定义的filter
context:include-filter:设置扫描哪些内容
type:根据注解来扫描
expression="org.springframework.stereotype.Component":扫描注解中带有Component注解的类
-->
<context:component-scan base-package="com.maven.spring" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
<!-- 示例2: 扫描包中的所有内容
context:exclude-filter:扫描哪些内容不进行扫描哪些内容
expression="org.springframework.stereotype.Component": 不扫描类上有Component注解的类
-->
<context:component-scan base-package="com.maven.spring" >
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
从spring容器中获取对象:
@Autowired:根据属性类型进行自动装配
@Autowired默认先按byType进行匹配,如果发现找到多个bean,则又按照byName方式进行匹配,如果还有多个,则报出异常
//Dao接口
public interface UserDao {
public void add();
}
//Dao接口实现类---------------------------------------------------------------------------------
@Repository(value = "userDaoImpl") //创建UserDaoImpl 对象
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("dao add.....");
}
}
//Service 类-------------------------------------------------------------------------------
/**
* 在注解里面 value属性值可以不写 如果不写就是 默认类名的第一个首字母小写
*/
@Service(value = "userService") //创建userService对象
public class UserService {
// 不需要添加set方法 添加注解属性即可
@Autowired //根据类型进行注入
private UserDao userDao;
public void add(){
userDao.add();
System.out.println("service add ....");
}
}
@Qualifier:根据名称进行注入
这个@Qualifier 注解的使用:和上面@Autowired 一起使用
@Qualifier :当这个接口有多个实现类的时候,就需要使用@Qualifier指定名称进行注入。
/UserDao 接口
public interface UserDao {
public void add();
}
//UserDao的实现类
@Repository(value = "userDaoImpl") //创建UserDaoImpl 对象
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("dao add.....");
}
}
//service层类
/**
* 在注解里面 value属性值可以不写 如果不写就是 默认类名的第一个首字母小写
*/
@Service(value = "userService") //创建userService对象
public class UserService {
// 不需要添加set方法 添加注解属性即可
@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl") //根据属性名进行注入属性
private UserDao userDao;
public void add(){
userDao.add();
System.out.println("service add ....");
}
}
@Resource和@Autowired注解都是用来实现依赖注入的。只是@AutoWried按by type自动注入,而@Resource默认按byName自动注入。
@Resource有两个重要属性,分别是name和type
spring将name属性解析为bean的名字,而type属性则被解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,如果使用type属性则使用byType的自动注入策略。如果都没有指定,则通过反射机制使用byName自动注入策略。
既不指定name属性,也不指定type属性,则自动按byName方式进行查找。如果没有找到符合的bean,则回退为一个原始类型进行查找,如果找到就注入。
<bean name="bucket" class="java.lang.String">
<constructor-arg value="${oos.bucketName}"/>
</bean>
<bean name="style" class="java.lang.String">
<constructor-arg value="${oos.styleName}"/>
</bean>
Resource(name="bucket")
private String bucketName;
@Resource(name="style")
private String styleName;
@Value:注入普通类型属性:给成员变量进行赋值
@Value(value = "字符串值")
private String name;
完全注解开发,使用配置类代替spring.xml文件
@Configuration //作为配置类 替代xml配置文件
// 指定扫描哪些包下的实体类
@ComponentScan(basePackages = {
"com.maven.spring"})
public class SpringConfig {
}
4.AOP(面向切面编程)
1、什么是 AOP
(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得
业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
(3)使用登录的例子举例说明
2.AOP的实现方式
1、连接点
类里面有哪些方法可以被增强,这些方法被称为连接点
2、切入点
实际被增强的方法,叫做切入点
3、通知(增强)
实际被增强的逻辑部分称为通知
通知的类型:
1.前置通知
2.后置通知
3.环绕通知
4.异常通知
5.最终通知
4、切面
是通知:是把通知应用到切入点的过程
JDK实现动态代理:
1.UserDao接口
public interface UserDao {
/**
* 实现两个数相加
* @param a 参数1
* @param b 参数2
* @return 返回两个数相加得到和
*/
public int add(int a,int b);
}
2.UserDaoImpl实现类
public class UserDaoImpl implements UserDao {
/**
* 实现两个数相加
*
* @param a 参数1
* @param b 参数2
* @return 返回两个数相加得到和
*/
@Override
public int add(int a, int b) {
return a+b;
}
}
3.UserDaoImpl的增强类
public class MyUserDaoProxy implements InvocationHandler {
private Object target=null;
// 传递过来需要被代理的对象
public MyUserDaoProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法执行之前
System.out.println("方法执行之前:"+method.getName()+"传递的参数"+ Arrays.toString(args));
//被增强的方法执行
System.out.println("执行被增强的方法");
Object res = method.invoke(target, args);
//方法后执行
System.out.println("方法执行之后:"+target);
return res;
}
}
4.测试方法:
//jdk动态代理
@Test
void jdkProxyMethods() {
UserDaoImpl userDao = new UserDaoImpl();
InvocationHandler handler = new MyUserDaoProxy(userDao);
UserDao proxy = (UserDao) Proxy.newProxyInstance(UserDaoImpl.class.getClassLoader(), userDao.getClass().getInterfaces(), handler);
int sum = proxy.add(5, 5);
}
2.AOP 操作(准备工作)
(1)AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使
用,进行 AOP 操作.
2、基于 AspectJ 实现 AOP 操作
(1)基于 xml 配置文件实现
(2)基于注解方式实现(使用)
3、在项目工程里面引入 AOP 相关依赖
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- spring 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- 测试依赖-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<!--aop 切面需要的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>net.sourceforge.cglib</groupId>
<artifactId>com.springsource.net.sf.cglib</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.aopalliance</groupId>
<artifactId>com.springsource.org.aopalliance</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.4.RELEASE</version>
</dependency>
</dependencies>
3.切入点表达式
切入点表达式作用:知道对哪个类里面的哪个方法进行增强
语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
举例 1:对 com.compass.dao.BookDao 类里面的 add 进行增强
execution(* com.compass.dao.BookDao.add(..))
举例 2:对 com.compass.dao.BookDao 类里面的所有的方法进行增强
execution(* com.compass.dao.BookDao.* (..))
举例 3:对 com.compass.dao 包里面所有类,类里面所有方法进行增强
execution(* com.compass.dao.*.* (..))
4.xml的方式实现Aop切面编程
1.Book类中有一个buy方法 ,现在需要做的就是在Book类中的方法执行之前执行其他的方法
public class Book {
public void buy(){
System.out.println("buy ...........");
}
}
2.Book类的增强类
public class BookProxy {
public void before(){
System.out.println("buy ...之前执行的方法");
}
public void after(){
System.out.println("buy ...之后执行的方法");
}
}
3.配置切入点
<bean id="book" class="com.maven.spring.aopanno.Book"></bean>
<bean id="bookProxy" class="com.maven.spring.aopanno.BookProxy"></bean>
<!--配置 aop 增强-->
<aop:config>
<!--切入点 被增强的方法-->
<aop:pointcut id="buy" expression="execution(* com.maven.spring.aopanno.Book.buy(..))"/>
<!--配置切面 order:当有多个增强类对同一个方法进行增强时,可以设置其优先级,数字类型值越小优先级越高-->
<aop:aspect ref="bookProxy" order="1">
<!--增强作用在具体的方法上 buy方法之前执行-->
<aop:before method="before" pointcut-ref="buy"/>
<!--增强作用在具体的方法上 buy方法之后执行-->
<aop:after method="after" pointcut-ref="buy"/>
</aop:aspect>
</aop:config>
5.注解的方式进行Aop增强
配置文件中需要开启:动态代理对象的功能
<!-- 开启Aspect生成代理对象 --> <aop:aspectj-autoproxy > </aop:aspectj-autoproxy>
1.被增强的类User类
//被增强类
@Component
public class User {
public void add(){
System.out.println("被增强方法:User类中的add()方法");
// int a = 12/0;
}
public int sum( int a,int b){
return a+b;
}
}
2.增强类UserProxy
//增强类
@Component //生成 UserProxy 对象
@Aspect() //生成代理对象
@Order(3) //当有多个增强类对同一个方法进行增强时,可以设置其优先级,数字类型值越小优先级越高
public class UserProxy {
//公共切入点的好处;使代码更加灵活,以后代码发生改变,只需要改此处就可以
@Pointcut(value = "execution(* com.maven.spring.aopanno.User.add(..))") //公共切入点抽取
public void pointcut(){
}
//前置通知
@Before(value = "pointcut()") //前置通知注解 (调用公共切入点的值)
public void before(){
System.out.println("before 前置通知...... 在被增强的方法之前执行");
}
//后置通知 add
@AfterReturning(value = "execution(* com.maven.spring.aopanno.User.add(..) )")
public void AfterReturning(){
System.out.println("AfterReturning 后置通知...... 在增强方法之后执行 当程序出现异常的时候不执行该方法 ");
}
//后置通知 sum returning 用于获取被增强方法执行后的返回值结果
@AfterReturning(value = "execution(* com.maven.spring.aopanno.User.sum(..) )" ,returning = "result")
public void AfterReturningSum(Object result){
System.out.println("AfterReturning 后置通知...... 在增强方法之后执行 当程序出现异常的时候不执行该方法 返回值: "+result);
}
//最终通知
@After(value = "execution(* com.maven.spring.aopanno.User.add(..) )")
public void after(){
System.out.println("after 最终通知...... 无论程序出现什么结果 都会执行该方法 在add方法返回结果后的时候执行");
}
//异常通知
@AfterThrowing(value = "execution(* com.maven.spring.aopanno.User.add(..) )")
public void AfterThrowing(){
System.out.println("AfterThrowing 异常通知......add方法出现异常的时候执行");
}
//环绕通知
@Around(value = "execution(* com.maven.spring.aopanno.User.add(..) )")
public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Around 环绕之前......在增强方法的前后都执行");
proceedingJoinPoint.proceed(); //调用被增强的add方法
System.out.println("Around 环绕之后......在增强方法的前后都执行");
}
}
3.测试:
//AOP 操作(AspectJ 注解)2 完全注解开发
@Test
void aopAnnotationMethods2() {
ApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
User user = context.getBean("user", User.class);
user.add();
user.sum(20,25);
}
6.公共切入点的抽取:
//公共切入点的好处;使代码更加灵活,以后代码发生改变,只需要改此处就可以
@Pointcut(value = "execution(* com.maven.spring.aopanno.User.add(..))") //公共切入点抽取
public void pointcut(){
}
//前置通知
@Before(value = "pointcut()") //前置通知注解 (调用公共切入点的值)
public void before(){
System.out.println("before 前置通知...... 在被增强的方法之前执行");
}
5.JdbcTemplate
1、什么是 JdbcTemplate
Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作
jdbcTemplate的基本入门使用:
1.环境配置准备:
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- spring 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- 测试依赖-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<!--aop 切面需要的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>net.sourceforge.cglib</groupId>
<artifactId>com.springsource.net.sf.cglib</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.aopalliance</groupId>
<artifactId>com.springsource.org.aopalliance</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.4.RELEASE</version>
</dependency>
<!--数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!--spring对jdbc的封装-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--spring整合其他框架的使用-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
2.spring.xml配置文件的配置
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.maven.spring"> </context:component-scan>
<!--配置数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="clone">
<property name="url" value="jdbc:mysql://localhost/book_ctiy?serverTimezone=GMT%2B8"> </property>
<property name="username" value="root"> </property>
<property name="password" value="2732195202"> </property>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"> </property>
</bean>
<!-- 配置 JdbcTemplate 对象,注入 DataSource -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
<!-- 将数据库连接池的信息注入到 dataSource 对象中-->
<property name="dataSource" ref="dataSource"> </property>
</bean>
2.Book实体类准备:
public class Book {
private String userId;
private String username;
private String ustatus;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUstatus() {
return ustatus;
}
public void setUstatus(String ustatus) {
this.ustatus = ustatus;
}
public Book(String userId, String username, String ustatus) {
this.userId = userId;
this.username = username;
this.ustatus = ustatus;
}
public Book() {
}
3.编写dao层接口
public interface BookDao {
/**
* 添加图书
* @param book 一个图书对象
*/
void add(Book book);
}
4.实现dao层的接口
@Service //创建BookService对象
public class BookService {
//注入Dao
@Autowired
private BookDao bookDao;
/**
* 添加图书
* @param book
*/
public void addBook(Book book){
bookDao.add(book);
}
}
4.对添加图书的操作进行测试
@Test
void addBook() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
BookService bookService = context.getBean("bookService", BookService.class);
Book book = new Book("123","admin","1");
bookService.addBook(book);
}
5.1 增删改查和批量操作
1.数据库表准备:
/*
Navicat Premium Data Transfer
Source Server : WindowsMysql
Source Server Type : MySQL
Source Server Version : 50732
Source Host : localhost:3306
Source Schema : component
Target Server Type : MySQL
Target Server Version : 50732
File Encoding : 65001
Date: 31/03/2021 14:43:02
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES (1, 'tom', 'tom', 'tom@qq.com', '123456');
INSERT INTO `t_user` VALUES (3, 'admin', 'admin123', 'admin@qq.com', '123456');
INSERT INTO `t_user` VALUES (4, 'root', 'root123', 'root@qq.com', '123456');
INSERT INTO `t_user` VALUES (5, 'test', 'test123', 'test@qq.com', '123456');
INSERT INTO `t_user` VALUES (6, '小明', '小明123', '小明@qq.com', '123456');
INSERT INTO `t_user` VALUES (7, '小王', '小王123', '小王@qq.com', '123456');
INSERT INTO `t_user` VALUES (8, 'compass', 'compass123', 'compass@qq.com', '123456');
INSERT INTO `t_user` VALUES (9, '卡夫卡', '卡夫卡123', '卡夫卡@qq.com', '123456');
SET FOREIGN_KEY_CHECKS = 1;
2.导入maven相关依赖:
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- spring 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--aop 切面需要的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>net.sourceforge.cglib</groupId>
<artifactId>com.springsource.net.sf.cglib</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.aopalliance</groupId>
<artifactId>com.springsource.org.aopalliance</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.4.RELEASE</version>
</dependency>
<!-- 测试依赖-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<!-- spring 测试依赖集成-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- c3p0连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!--spring对jdbc的封装-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--spring整合其他框架的使用-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
3.SpringConfig.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.springjdbc.crud"> </context:component-scan>
<!--配置数据库连接池-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost/component?serverTimezone=GMT%2B8"> </property>
<property name="username" value="root"> </property>
<property name="password" value="自己数据库密码"> </property>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"> </property>
</bean>
<!-- 配置 JdbcTemplate 对象,注入 DataSource -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
<!-- 将数据库连接池的信息注入到 dataSource 对象中-->
<property name="dataSource" ref="myDataSource"> </property>
</bean>
</beans>
4.User(实体类)
public class User {
private Integer id;
private String username;
private String password;
private String email;
private String phone;
public User(Integer id, String username, String password, String email, String phone) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.phone = phone;
}
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
5.dao层接口代码:
import java.util.List;
public interface UserDao {
/**
* 添加一个user
* @param user user对象
* @return 影响的行数
*/
int addUser(User user);
/**
* 删除一个user
* @param id userId
* @return 影响的行数
*/
int deleteUserById(String id);
/**
* 查询所有的user
* @return List集合
*/
List<User> queryUserAll();
/**
* 修改user信息
* @param user 需要修改的user对象信息
* @return 影响的行数
*/
int updateUserById(User user);
/**
* 根据id查询user
* @param id userId
* @return 返回一个 user对象
*/
User queryUserOne(int id);
/**
* 统计表中有多少个用户
* @return 返回int类型的数量
*/
int getUserCount();
/**
* 按条件模糊查询
* @return 返回一个List集合
*/
List<User> selectUserLike(String username);
/**
* 分页查询
* @return 返回一个List集合
*/
List<User> selectUserPage(int begin, int stop );
/**
* 批量添加数据
* @param batchArgs list集合数组
*/
void batchAddUser(List<Object[]> batchArgs);
/**
* 批量修改数据
* @param batchArgs list集合数组
*/
void batchUpdateUser(List<Object[]> batchArgs);
/**
* 批量删除数据
* @param batchArgs list集合数组
*/
void batchDeleteUser(List<Object[]> batchArgs);
}
6.dao层的实现类
import com.springjdbc.crud.dao.UserDao;
import com.springjdbc.crud.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int addUser(User user) {
String sql = "insert into t_user(id,username,password,email,phone) values(?,?,?,?,?)";
Object args[] = {
user.getId(),user.getUsername(),user.getPassword(),user.getEmail(),user.getPhone(),};
int updateCount = jdbcTemplate.update(sql, args);
return updateCount;
}
@Override
public int deleteUserById(String id) {
String sql = "delete from t_user where id=?";
int deleteCount = jdbcTemplate.update(sql, id);
return deleteCount;
}
@Override
public List<User> queryUserAll() {
String sql = "select * from t_user ";
List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class));
return userList;
}
@Override
public User queryUserOne( final int id) {
String sql="select id,username,password,email,phone from t_user where id =?";
final User user = new User();
//将结果集数据行中的数据抽取到forum对象中
jdbcTemplate.query(sql, new Object[]{
id}, new RowCallbackHandler() {
public void processRow(ResultSet resultSet) throws SQLException {
user.setId(id);
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
user.setEmail(resultSet.getString("email"));
user.setPhone(resultSet.getString("phone"));
}
});
return user;
}
/**
* 统计表中有多少个用户
*
* @return 返回int类型的数量
*/
@Override
public int getUserCount() {
String sql =" select count(*) from t_user";
int userCount = jdbcTemplate.queryForObject(sql, Integer.class);
return userCount;
}
@Override
public int updateUserById(User user) {
String sql = "update t_user set username=?,password=?,email=?,phone=? where id=?";
Object args [] ={
user.getUsername(),user.getPassword(),user.getEmail(),user.getPhone(),user.getId()};
int updateCount = jdbcTemplate.update(sql, args);
return updateCount;
}
@Override
public List<User> selectUserLike(String username) {
String sql = "select id,username,password,email,phone from t_user where username like ?";
List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class),username);
return userList;
}
@Override
public List<User> selectUserPage(int begin, int stop) {
String sql ="select id,username,password,email,phone from t_user limit ? , ?";
List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class), begin, stop);
return userList;
}
@Override
public void batchAddUser(List<Object[]> batchArgs) {
String sql = "insert into t_user( username, password, email, phone) values(?,?,?,?)";
int[] args = jdbcTemplate.batchUpdate(sql, batchArgs);
}
@Override
public void batchUpdateUser(List<Object[]> batchArgs) {
String sql = "update t_user set username=?,password=?,email=?,phone=? where id=?";
int[] args = jdbcTemplate.batchUpdate(sql, batchArgs);
}
@Override
public void batchDeleteUser(List<Object[]> batchArgs) {
String sql = "delete from t_user where id=?";
int[] args = jdbcTemplate.batchUpdate(sql, batchArgs);
}
}
7.service层接口代码:
import java.util.List;
/**
* @author compass
* @version 1.0
* @date 2021-03-31 12:32
*/
public interface UserService {
/**
* 添加一个user
* @param user user对象
* @return 影响的行数
*/
int addUser(User user);
/**
* 删除一个user
* @param id userId
* @return 影响的行数
*/
int deleteUserById(String id);
/**
* 查询所有的user
* @return List集合
*/
List<User> queryUserAll();
/**
* 修改user信息
* @param user 需要修改的user对象信息
* @return 影响的行数
*/
int updateUserById(User user);
/**
* 根据id查询user
* @param id userId
* @return 返回一个 user对象
*/
User queryUserOneById(int id);
/**
* 统计表中有多少个用户
* @return 返回int类型的数量
*/
int getUserCount();
/**
* 按条件模糊查询
* @return 返回一个List集合
*/
List<User> selectUserLike(String username);
/**
* 分页查询
* @return 返回一个List集合
*/
List<User> selectUserPage(int begin, int stop );
/**
* 批量添加数据
* @param batchArgs list集合数组
*/
void batchAddUser(List<Object[]> batchArgs);
/**
* 批量修改数据
* @param batchArgs list集合数组
*/
void batchUpdateUser(List<Object[]> batchArgs);
/**
* 批量删除数据
* @param batchArgs list集合数组
*/
void batchDeleteUser(List<Object[]> batchArgs);
}
8.service层实现:
import java.util.List;
/**
* @author compass
* @version 1.0
* @date 2021-03-31 12:33
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public int addUser(User user) {
return userDao.addUser(user);
}
@Override
public int deleteUserById(String id) {
return userDao.deleteUserById(id);
}
@Override
public List<User> queryUserAll() {
return userDao.queryUserAll();
}
@Override
public int updateUserById(User user) {
return userDao.updateUserById(user);
}
/**
* 根据id查询user
*
* @param id userId
* @return 返回一个 user对象
*/
@Override
public User queryUserOneById(int id) {
return userDao.queryUserOne(id);
}
/**
* 统计表中有多少个用户
*
* @return 返回int类型的数量
*/
@Override
public int getUserCount() {
return userDao.getUserCount();
}
/**
* 按条件模糊查询
*
* @param username
* @return 返回一个List集合
*/
@Override
public List<User> selectUserLike(String username) {
return userDao.selectUserLike("%"+username+"%");
}
/**
* 分页查询
*
* @param begin
* @param stop
* @return 返回一个List集合
*/
@Override
public List<User> selectUserPage(int begin, int stop) {
return userDao.selectUserPage(begin,stop);
}
/**
* 批量添加数据
*
* @param batchArgs list集合数组
*/
@Override
public void batchAddUser(List<Object[]> batchArgs) {
userDao.batchAddUser(batchArgs);
}
/**
* 批量修改数据
*
* @param batchArgs list集合数组
*/
@Override
public void batchUpdateUser(List<Object[]> batchArgs) {
userDao.batchUpdateUser(batchArgs);
}
/**
* 批量删除数据
*
* @param batchArgs list集合数组
*/
@Override
public void batchDeleteUser(List<Object[]> batchArgs) {
userDao.batchDeleteUser(batchArgs);
}
}
9.测试service代码:
import java.util.ArrayList;
import java.util.List;
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:springConfig.xml")
public class UserServiceTest {
@Autowired
private UserService userService;
// 添加一条数据
@Test
void addUserTest(){
User user = new User(null,"卡夫卡","卡夫卡123","卡夫卡@qq.com","123456");
Integer addUserCount = userService.addUser(user);
}
// 查询所有数据
@Test
void queryUserTest(){
List<User> userList = userService.queryUserAll();
for (User user : userList) {
System.out.println(user);
}
}
// 删除一条数据
@Test
void deleteUserTest(){
Integer integer = userService.deleteUserById("2");
System.out.println(integer);
}
// 修改一条数据
@Test
void updateUserTest(){
User user = new User(1,"kfk","kfk123","tom@qq.com","123456");
Integer addUserCount = userService.updateUserById(user);
}
// 按照id查询一条数据
@Test
void queryUserOneById(){
User user = userService.queryUserOneById(1);
System.out.println(user);
}
// 获取表中有多少个user用户
@Test
void getUserCount() {
int userCount = userService.getUserCount();
System.out.println(userCount);
}
// 根据 username 字段模糊查询
@Test
void selectUserLike(){
List<User> userList = userService.selectUserLike("ad");
for (User user : userList) {
System.out.println(user);
}
}
// 分页查询
@Test
void selectUserPage() {
List<User> userList = userService.selectUserPage(0, 3);
for (User user : userList) {
System.out.println(user);
}
}
// 批量添加
@Test
void batchAddUser() {
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {
"adminOne","admin123","admin@qq.com1","123456"};
Object[] o2 = {
"adminTwo","admin123","admin@qq.com1","123457"};
Object[] o3 = {
"adminThree","admin123","admin@qq.com1","123458"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
userService.batchAddUser(batchArgs);
}
// 批量修改
@Test
void batchUpdateUser() {
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {
"adminOne1","admin123","admin@qq.com1","123456",10};
Object[] o2 = {
"adminTwo2","admin123","admin@qq.com1","123457",11};
Object[] o3 = {
"adminThree3","admin123","admin@qq.com1","123458",12};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
userService.batchUpdateUser(batchArgs);
}
// 批量删除
@Test
void batchDeleteUser() {
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {
10};
Object[] o2 = {
11};
Object[] o3 = {
12};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
userService.batchDeleteUser(batchArgs);
}
}
注意点补充:
1.不同的数据库连接池,它的urt,password,user,driverClassName这个几个值是不也一样的。
2.查询单条数据和查询全部的方式是不一样的,虽然利用BeanPropertyRowMapper的方式既可以查询全部,也可以查询单条数据,但是不推荐在查询单条数据的时候使用 BeanPropertyRowMapper ,因为BeanPropertyRowMapper是直接把全部数据加载出来,而利用结果集封装的方式是按需加载。
3.需要注意本地数据库和mysql驱动包版本是否一致,如果是高版本的时候,在写数据库的url的时候记得的加上时区,dirver也是需要注意的。
高版本的dirver写法:com.mysql.cj.jdbc.Driver
低版本的dirver写法:com.mysql.jdbc.Driver
原生jdbc获取数据库连接对象的方式:
public class JDBCTest {
public static void main(String[] args) {
Connection connection = getConnection();
System.out.println(connection);
}
public static Connection getConnection(){
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String username="root";
String url="jdbc:mysql://localhost/compass?serverTimezone=GMT%2B8";
String password="123456";
Connection connection = null;
try {
connection = DriverManager.getConnection(url, username, password);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return connection;
}
}
6.spring事务
1.事务的四个特性:(只有dml语句才有事务,也就是增删改语句)
-
原子性(atomic)多个事务单元捆绑在一起执行,看做是一个整体执行计划,只要其中有一个事务执行失败,那么这个执行计划就失败,进行回滚,数据恢复到计划执行之前的状态。如果这个计划中的每个事务都执行成功,那么这个计划执行成功,数据进行修改。
-
一致性(consistent),一旦一个事务结束了,不管成功还是失败,系统所处的状态和它的业务规则是一致的。也就是说,数据应当不会被破坏。
-
隔离性(isolation),事务应当允许多名用户操作同一个数据,一个用户的操作不会和其他用户的操作相互影响。因此,事务必须是隔离的,防止并行读写同一个数据的情况发生。注意,隔离通常意味着要锁定数据库的表或行(不隔离会造成,脏读,可重复读,幻读)。
-
持久性(durable),一旦事务完成,事务的结果应当持久化。将修改后的数据保存到电脑磁盘,就算马上停电也能正常保存数据。
- MYSQL控制台事务操作
- 开启事务:start transaction
- 设置保存点:savepoint(可以设置多个保存点)
- 回退到某个保存点:rollback to saveName
- 回退全部事务,回到开启事务之前的状态:rollback
- 提交事务(事务一旦提交,无法再进行回滚):commit
3.事务的隔离级别三个读的问题:
-
脏读: 事务A进行修改数据但是,还没有进行提交,事务B进行读取事务A修改的数据,但是过了一会儿事务A提交了事务,这就导致了事务B读取到的数据不正确,就造成了脏读。
-
不可重复读: 一个未提交的A事务,可以多次读取同一条数据,假设在事务A在多次读取的途中事务B修或删除改了事务A读取的那一条数据,这就导致了事务A每次读取到的数据都不一样。
-
幻读: 一个事务A先后读取一个范围的记录,但两次读取的纪录数不同,我们称之为幻象读,在事务A读取数据的途中,事务B去增加了一条数据,导致事务A两次查询到的数据记录总数是不一样的,造成幻读。
解决事务隔离级别造成的问题:
MYSQL默认的隔离级别:REPEATABLE-READ(可重复读)
使用串行化会导致执行效率变低,因为隔离会涉及到锁表或者是锁行。
4.事务的传播行为:多个事务方法之间进行调用,如何进行管理?
5.timeout:超时时间 单位是 秒
-
事务需要在一定时间内进行提交,如果不提交进行回滚
-
默认值是 -1 ,设置时间以秒单位进行计算
5、readOnly:是否只读
-
读:查询操作,写:添加修改删除操作
-
readOnly 默认值 false,表示可以查询,可以添加修改删除操作
-
设置 readOnly 值是 true,设置成 true 之后,只能查询
6、rollbackFor:回滚 (
- 设置出现哪些异常进行事务回滚
7、noRollbackFor:不回滚
- 设置出现哪些异常不进行事务回滚
6.2事务操作(XML式事务管理)
1.spring.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.maven.spring"> </context:component-scan>
<!--配置数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="clone">
<property name="url" value="jdbc:mysql://localhost/book_ctiy?serverTimezone=GMT%2B8"> </property>
<property name="username" value="root"> </property>
<property name="password" value="2732195202"> </property>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"> </property>
</bean>
<!-- 配置 JdbcTemplate 对象,注入 DataSource -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
<!-- 将数据库连接池的信息注入到 dataSource 对象中-->
<property name="dataSource" ref="dataSource"> </property>
</bean>
<!-- spring配置文件配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"> </property>
</bean>
<!--2 配置通知-->
<tx:advice id="txAdvice">
<!--配置事务参数-->
<tx:attributes>
<!--指定哪种规则的方法上面添加事务 account.* 以account开头的方法配置事务-->
<tx:method name="accountMoney" propagation="REQUIRED" isolation="DEFAULT"/>
<!-- <tx:method name="account*"/>-->
</tx:attributes>
</tx:advice>
<!--3 配置切入点和切面-->
<aop:config>
<!--配置切入点 UserDaoService类中的所有方法都配置事务 -->
<aop:pointcut id="pt" expression="execution(* com.maven.spring.service.UserDaoService.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
</beans>
2.UserService中的方法:
public class UserDaoService {
@Autowired
private UserDao userDao;
// 在UserService中同时调用增加钱的方法和同时减少钱的方法
public void accountMoney() {
// 1.开启事务操作
//admin 少100 root多100
// 2.事务开启事务
//减钱
userDao.reduceMoney();
//模拟异常 如果此处出现异常,那么将事务回滚,admin不少钱,root也不增加钱
int i = 20/0;
//加钱
userDao.addMoney();
//3.没有异常 提交事务
//4.出现异常 事务回滚
}
}
6.3事务操作(全注解式事务管理)
1.使用配置类代替Spring.xml文件
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration //配置类
@ComponentScan(basePackages = "com.maven.spring") //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/book_ctiy?serverTimezone=GMT%2B8");
dataSource.setUsername("root");
dataSource.setPassword("2732195202");
return dataSource;
}
//创建JdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
//到ioc容器中根据类型找到dataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入dataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
// 创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
2.在UserService类中的方法中添加上Transaction注解
@Service
public class UserDaoService {
@Autowired
private UserDao userDao;
//开启事务管理 可以添加到类上 可以添加到方法上 添加到类上就是类中所有的方法都开启事务管理
@Transactional( isolation = Isolation.DEFAULT ,propagation = Propagation.REQUIRED)
public void accountMoney() {
// 1.开启事务操作
//admin 少100 root多100
// 2.事务开启事务
//减钱
userDao.reduceMoney();
//模拟异常
int i = 20/0;
//加钱
userDao.addMoney();
//3.没有异常 提交事务
//4.出现异常 事务回滚
}
}
//测试 纯注解事务管理
@Test
void accountMoneyAnnotations() {
ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
UserDaoService userDaoService = context.getBean("userDaoService", UserDaoService.class);
userDaoService.accountMoney();
}
注意的是:注解的方式和xml的方式不能同时一起使用,只能任选其一,比如使用xml的方式在spring.xml配置文件中指定了某些个方法的事务处理,就不能在该方法上加上 @Transactional 注解。
7.Spring5 集成测试环境
spring基础junit测试环境:
1.导入spring.test依赖
<!-- spring集成测试依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
2.junit5测试环境的集成
<!--junit 测试依赖-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
3.在需要的测试类上加上@ExtendWith和@ContextConfiguration注解
也可以使用一个复合注解@SpringJUnitConfig( locations = “classpath:applicationContext.xml”)代替这两个注解
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class BookServiceTest {
@Autowired
private BookService bookService;
@Test
public void queryAllBook(){
List<Book> bookList = bookService.queryAll();
for (Book book : bookList) {
System.out.println(book);
}
}
}
3.junit4集成spring测试环境
<!-- spring集成测试依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
在需要测试类上加上@RunWith和@ContextConfiguration注解
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class BookServiceTest {
@Autowired
private BookService bookService;
@Test
public void queryAllBook(){
List<Book> bookList = bookService.queryAll();
for (Book book : bookList) {
System.out.println(book);
}
}
SpringMVC:
springMVC请求流程图:
1.静态资源映射问题
1.在springmvc.xml配置文件中加上resources标签
<mvc:resources location="/static/" mapping="/static/**" />
注意:static需要放在webapp目录下,static中放的是js,css,img等静态资源,就是说static中的资源不会别拦截,会被放行,可访问
2.web.xml中配置的url-pattern地址为:"/" ;
<!--
1.*.mvc:拦截所有请求地址中带有.mvc的请求
2./:拦截所有的请求资源(html.静态资源),jsp除外
3./*:拦截所有的资源,包括jsp
-->
<url-pattern>/</url-pattern>
3.使用"/"的好处就是;Controller接口中的方法不再需要写 *.do 或者是 *.mvc等后缀
2.springMVC拦截器
1.在springMVC.xml中配置拦截器对象:可以配置多个拦截器对象,再写一个interceptor标签就行
<mvc:interceptors>
<!-- 声明一个一个拦截器 -->
<mvc:interceptor>
<!-- 需要拦截的请求url -->
<mvc:mapping path="/**"/>
<!-- 放行的请求 -->
<mvc:exclude-mapping path="/user/**"/>
<mvc:exclude-mapping path="/book/**"/>
<!-- 声明拦截器对象 -->
<bean class="com.books.handler.LoginPermissions"> </bean>
</mvc:interceptor>
</mvc:interceptors>
-
/**:表示拦截所有的请求
-
/admin/**:表示拦截admin模块下的所有请求
-
/*.mvc:b表示拦截所有请求地址中带有.mvc的请求地址
2.写一个拦截器类实现HandlerIncterceptor接口,并且实现里面的三个方法
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginPermissions implements HandlerInterceptor {
/**
* preHandle叫做预处理方法。
* 重要:当preHandle返回true 请求可以被处理。
* preHandle返回false,请求到此方法就截止。
* 特点:
* 1.方法在控制器方法(也就是我们的Controller层里面的方法)之前先执行的。用户的请求首先到达此方法
* 2.在这个 方法中可以获取请求的信息, 验证请求是否符合要求。
* 可以验证用户是否登录, 验证用户是否有权限访问某个连接地址(url)。
* 如果验证失败,可以截断请求,请求不能被处理。
* 如果验证成功,可以放行请求,此时控制器方法才能执行。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取session中设置的用户名,如果为null就说明没有登录,先让其登录才可以进行访问,session的值在登录验证成功时设置
String username = (String)request.getSession().getAttribute("username");
if (username==null||username==""){
// WEB-INF下的资源外部请求不能直接进行访问,只有在servlet内进行请求转发才可以进行访问
request.getRequestDispatcher("/WEB-INF/view/Login.jsp").forward(request,response);
return false;
}else {
// return true表示 放行该请求 false:表示拦截该请求,不允许进行访问
return true;
}
}
/**
* 特点:
* 1.在处理器方法之后执行的(MyController.doSome())
* 2.能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView中的数据和视图,可以影响到最后的执行结果。
* 3.主要是对原来的执行结果做二次修正,
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 特点:
* 1.在请求处理完成后执行的。框架中规定是当你的视图处理完成后,对视图执行了forward。就认为请求处理完成。
* 2.一般做资源回收工作的, 程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
拦截器和过滤器的区别:
1.过滤器是servlet中的对象, 拦截器是框架中的对象
2.过滤器实现Filter接口的对象, 拦截器是实现HandlerInterceptor
3.过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的。 拦截器是用来验证请求的,能截断请求。
4.过滤器是在拦截器之前先执行的。
5.过滤器是tomcat服务器创建的对象,拦截器是springmvc容器中创建的对象
6.过滤器是一个执行时间点。拦截器有三个执行时间点
7.过滤器可以处理jsp,js,html等等
拦截器是侧重拦截对Controller的对象。 如果你的请求不能被DispatcherServlet接收, 这个请求不会执行拦截器内容
8.拦截器拦截普通类方法执行,过滤器过滤servlet请求响应
3.ssm文件上传和下载模板代码
1.文件上传
@Controller
public class UploadIFileController {
// 单个文件上传
@PostMapping("/uploadFile1.mvc")
public ModelAndView uploadFile1(MultipartFile file, HttpSession session) throws IOException {
ModelAndView view = new ModelAndView();
// 得到文件名称
String filename=file.getOriginalFilename();
System.out.println("文件名称:"+filename);
if (!file.isEmpty()){
// 判断文件的后缀
if (filename.endsWith(".jpg")||filename.endsWith(".png")||filename.endsWith(".txt"));
//设置文件的保存路径
String savePath="文件保存路径";
File srcFile = new File(savePath,filename);
// 执行文件保存操作
file.transferTo(srcFile);
view.setViewName("forward:/uploadSuccess.jsp");
}else {
view.setViewName("forward:/uploadFailed.jsp");
}
return view;
}
// 多文件上传
@PostMapping("/uploadFile2.mvc")
public ModelAndView uploadFile2(MultipartFile[] file,HttpSession session) throws IOException {
ModelAndView view = new ModelAndView();
//设置文件的保存路径
String savePath="文件报错路径";
String[] filenames = new String[file.length];
// 只要上传过来的文件为空或者是不符合指定类型的都会上传失败
for (int i = 0; i <filenames.length ; i++) {
// 判断上传过来的文件是否为空
if (!file[i].isEmpty()){
String filename=file[i].getOriginalFilename();
// 判断文件类型
if (filename.endsWith(".txt")||filename.endsWith(".jpg")||filename.endsWith(".png")){
// 创建一个文件对象
File srcFile = new File(savePath, filename);
// 执行保存文件操作
file[i].transferTo(srcFile);
view.setViewName("forward:/uploadSuccess.jsp");
}else {
view.setViewName("forward:/uploadSuccess.jsp");
}
}else {
view.setViewName("forward:/uploadFailed.jsp");
}
}
return view;
}
}
文件上传前端对应的代码:
<div>
<p style="text-align: center">文件上传(单个文件单字段上传)</p>
<form action="${pageContext.request.contextPath}/uploadFile1.mvc" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="提交">
</form>
</div>
<div>
<p style="text-align: center">文件上传(多文件单字段上传)</p>
<form action="${pageContext.request.contextPath}/uploadFile2.mvc" method="post" enctype="multipart/form-data">
<input type="file" name="file" multiple="multiple">
<input type="submit" value="提交">
</form>
</div>
<div>
<p style="text-align: center">文件上传(多文件多字段上传)</p>
<form action="${pageContext.request.contextPath}/uploadFile2.mvc" method="post" enctype="multipart/form-data">
<input type="file" name="file" >
<input type="file" name="file" >
<input type="submit" value="提交">
</form>
</div>
2.文件下载:
@Controller
public class DownloadController {
// 非中文名称文件下载
@GetMapping("/download1.mvc")
public ResponseEntity <byte[]> fileDownload1(String filename,HttpServletRequest request) throws IOException {
// 需要下载的是那个文件:String filename
String path="存放文件的父目录地址";
File file = new File(path,filename);
HttpHeaders header = new HttpHeaders();
header.setContentDispositionFormData("attachment",filename);
header.setContentType(MediaType.APPLICATION_OCTET_STREAM);
ResponseEntity<byte[]> result = new ResponseEntity<>(FileUtils.readFileToByteArray(file), header, HttpStatus.OK);
return result;
}
// 中文名称文件下载
@GetMapping("/download2.mvc")
public ResponseEntity <byte[]> fileDownload2(String filename,HttpServletRequest request) throws IOException {
String filename
String path="存放文件的父目录地址";
filename = filename.replace("_", "%");
filename= URLDecoder.decode(filename,"UTF-8");
String downloadFile="";
if (request.getHeader("USER-AGENT").toLowerCase().indexOf("msie")>0){
filename= URLEncoder.encode(filename,"UTF-8");
downloadFile=filename.replaceAll("+","%20");
}else {
downloadFile=new String(filename.getBytes("UTF-8"),"ISO-8859-1");
}
File file = new File(path,filename);
HttpHeaders header = new HttpHeaders();
header.setContentDispositionFormData("attachment",downloadFile);
header.setContentType(MediaType.APPLICATION_OCTET_STREAM);
ResponseEntity<byte[]> result = new ResponseEntity<>(FileUtils.readFileToByteArray(file), header, HttpStatus.OK);
return result;
}
}
4.全局异常处理
1.SpringMVC 框架处理异常的常用方式:使用@ExceptionHandler 注解处理异常。
2.@ControllerAdvice : 控制器增强(也就是说给控制器类增加功能–异常处理功能)
3.使用注解@ExceptionHandler 可以将一个方法指定为异常处理方法。该注解只有一个可选属性 value,为一个 Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。而被注解的方法,其返回值可以是 ModelAndView、String,或 void,方法名随意,方法参数可以是 Exception 及其子类对象、HttpServletRequest、HttpServletResponse 等。系统会
自动为这些方法参数赋值。对于异常处理注解的用法,也可以直接将异常处理方法注解于 Controller 之中。
4.自定义异常类
//当年龄有问题时,抛出的异常
public class AgeException extends MyUserException {
public AgeException() {
super();
}
public AgeException(String message) {
super(message);
}
}
-------------------------------------------------------
//表示当用户的姓名有异常,抛出NameException
public class NameException extends MyUserException {
public NameException() {
super();
}
public NameException(String message) {
super(message);
}
}
--------------------------------------------------
public class MyUserException extends Exception {
public MyUserException() {
super();
}
public MyUserException(String message) {
super(message);
}
}
5.发生异常后该如何进行处理:
**
* @ControllerAdvice : 控制器增强(也就是说给控制器类增加功能--异常处理功能)
* 位置:在类的上面。
* 特点:必须让框架知道这个注解所在的包名,需要在springmvc配置文件声明组件扫描器。
* 指定@ControllerAdvice所在的包名
*/
@ControllerAdvice
public class GlobalExceptionHandler {
//定义方法,处理发生的异常
/*
处理异常的方法和控制器方法的定义一样, 可以有多个参数,可以有ModelAndView,
String, void,对象类型的返回值
形参:Exception,表示Controller中抛出的异常对象。
通过形参可以获取发生的异常信息。
@ExceptionHandler(异常的class):表示异常的类型,当发生此类型异常时,
由当前方法处理
*/
@ExceptionHandler(value = NameException.class)
public ModelAndView doNameException(Exception exception){
//处理NameException的异常。
/*
异常发生处理逻辑:
1.需要把异常记录下来, 记录到数据库,日志文件。
记录日志发生的时间,哪个方法发生的,异常错误内容。
2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
3.给用户友好的提示。
*/
ModelAndView mv = new ModelAndView();
mv.addObject("msg","姓名必须是admin,其它用户不能访问");
mv.addObject("ex",exception);
mv.setViewName("nameError");
return mv;
}
//处理AgeException
@ExceptionHandler(value = AgeException.class)
public ModelAndView doAgeException(Exception exception){
//处理AgeException的异常。
/*
异常发生处理逻辑:
1.需要把异常记录下来, 记录到数据库,日志文件。
记录日志发生的时间,哪个方法发生的,异常错误内容。
2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
3.给用户友好的提示。
*/
ModelAndView mv = new ModelAndView();
mv.addObject("msg","你的年龄不能大于80");
mv.addObject("ex",exception);
mv.setViewName("ageError");
return mv;
}
//处理其它异常, NameException, AgeException以外,不知类型的异常
@ExceptionHandler
public ModelAndView doOtherException(Exception exception){
//处理其它异常
ModelAndView mv = new ModelAndView();
mv.addObject("msg","你的年龄不能大于80");
mv.addObject("ex",exception);
mv.setViewName("defaultError");
return mv;
}
}
5.springMVC返回值处理
➢第一种:ModelAndView
➢ 第二种:String
➢ 第三种:无返回值 void
➢ 第四种:返回自定义类型对象
1.maven依赖:
dependencies>
<!-- junit测试依赖 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<!-- servlet依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- springMVC依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
<scope>compile</scope>
</dependency>
<!--jstl标签库包-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<!-- jackson 依赖-->
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
2.maven依赖:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.test"></context:component-scan>
<!--声明 配置springMVC视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀:视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/" />
<!--后缀:视图文件的扩展名-->
<property name="suffix" value=".jsp" />
</bean>
<!--读写JSON的支持(Jackson)-->
<mvc:annotation-driven />
</beans>
3.返回ModelAndView(常用)
使用ModelAndView返回数据:返回的数据是在request中的
想当于view.addObject("messages","hello world");
是等同于 request.setAttribute("messages","hello world");
的
Controller
@RequestMapping(value = "/user/")
public class UserController {
// 1.使用ModelAndView返回数据:返回的数据是在request中的
@GetMapping(value = "returnModelAndView.mvc")
public ModelAndView returnModelAndView(HttpServletRequest request){
ModelAndView view = new ModelAndView();
view.addObject("messages","hello world");
view.setViewName("show");
return view;
}
}
前端使用:${requestScope.messages}接受数据
4.返回值为String;(配合视图解析器返回视图)
若要跳转的资源为内部资源,则视图解析器可以使用 InternalResourceViewResolver 内部资源视图解析器。此时处理器方法返回的字符串就是要跳转页面的文件名去掉文件扩展名后的部分。这个字符串与视图解析器中的 prefix、suffix 相结合,即可形成要访问的 UR
@Controller
@RequestMapping(value = "/user/")
public class UserController {
// 使用视图解析器加返回String用于跳转页面
@GetMapping("returnStringView.mvc")
public String returnStringView(){
return "index";
}
}
5.返回 void(了解)
对于处理器方法返回 void 的应用场景,AJAX 响应.
若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aQjKMCjg-1621776193285)(C:\Users\14823\AppData\Roaming\Typora\typora-user-images\image-20210520121850755.png)]
@Controller
@RequestMapping(value = "/user/")
public class UserController {
// 无返回值,使用HttpServletRequest对象跳转页面,session传输数据
@GetMapping("returnVoid.mvc")
public void returnVoid(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws ServletException, IOException {
request.getRequestDispatcher("/WEB-INF/view/index.jsp").forward(request,response);
session.setAttribute("message","Hello Word");
}
}
6.返回自定义类型对象(重点掌握这个)
处理器方法也可以返回 Object 对象。这个 Object 可以是 Integer,String,自定义对象,Map,List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。返回对象,需要使用@ResponseBody 注解,将转换后的 JSON 数据放入到响应体中。
6.1 返回自定义对象,返回的是json字符串
@Controller
@RequestMapping(value = "/user/")
public class UserController {
// 用于跳转到 ajax.jsp页面
@GetMapping("ajax.mvc")
public String ajax(){
return "ajax";
}
// 用于响应ajax请求
@ResponseBody
@PostMapping("AjaxReturnObject.mvc")
public User AjaxReturnObject(User user){
User myUser = new User(user.getId(),user.getUsername(),user.getPassword(),user.getEmail(),user.getTelephone());
// 返回json字符串
return myUser;
}
}
<script type="text/javascript">
$(function(){
$("#btn").click(function(){
alert("button click");
$.ajax({
url:"/user/AjaxReturnObject.mvc",
data:{
id:1,
username:"admin",
password:"root",
email:"admin@qq.com",
telephone:"123456"
},
type:"post",
dataType:"json",
success:function(data){
// 返回的数据都放在data中
console.log(data);
document.getElementById("id").innerHTML=data.id;
document.getElementById("username").innerHTML=data.username;
document.getElementById("password").innerHTML=data.password;
document.getElementById("email").innerHTML=data.email;
document.getElementById("telephone").innerHTML=data.telephone;
}
});
});
});
</script>
注意点:如果dataType:"text"是这样的,是一个json字符串,需要将json字符串转化为js对象。
js对象转json字符串:var jsonStr = JSON.stringify(“需要转换的js对象”);
json字符串转js对象: var data =JSON.parse(“需要转换的json字符串”);
6.2 返回List集合:list集合返回到前端就是json数组:
@Controller
@RequestMapping(value = "/user/")
public class UserController {
// 返回List集合
@ResponseBody
@GetMapping("AjaxReturnList.mvc")
public List<User> AjaxReturnList(){
List<User> userList= new ArrayList<User>();
User user1 = new User(1,"admin1","root1","admin@qq.com1","1001");
User user2 = new User(2,"admin2","root2","admin@qq.com2","1002");
User user3 = new User(3,"admin3","root3","admin@qq.com3","1003");
User user4 = new User(4,"admin4","root4","admin@qq.com4","1004");
userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);
// 返回json数组
return userList;
}
}
如果返回对象,按utf-8编码。如果返回String,默认按iso8859-1编码,页面可能出现乱码。因此在注解中我们可以手动修改编码格式,例如@RequestMapping(value="/ajax/user",produces=“text/html; charset=utf-8”)
6.3返回Map集合:
@ResponseBody
@GetMapping("AjaxReturnMap.mvc")
public Map<String,Object> AjaxReturnMap(){
Map<String, Object> map = new HashMap<>();
User user1 = new User(1,"admin","root","admin@qq.com","1001");
User user2 = new User(2,"admin2","root2","admin@qq.com2","1002");
User user3 = new User(3,"admin3","root3","admin@qq.com3","1003");
User user4 = new User(4,"admin4","root4","admin@qq.com4","1004");
map.put("user1",user1);
map.put("user2",user2);
map.put("user3",user3);
map.put("user4",user4);
return map;
}
6.4返回String字符串:
// 直接返回字符串
@ResponseBody
@GetMapping("AjaxReturnString.mvc")
public String AjaxReturnString(){
return "hello world";
}
-
如果方法上没有 @ResponseBody注解那就是返回视图,有就是返回字符串
-
需要注意的是如果返回的是String字符串,
dataType:"text"
是text,否则接收值的时候是有问题的。
6.springMVC注解开发
1.RequestMapping放置在类上指定模块名称:
2.value 属性用于定义所匹配请求的 URI,value值是一个String类型的数组,可以存放多个请求地址
一个@Controller 所注解的类中,可以定义多个处理器方法。当然,不同的处理器方法所匹配的 URI 是不同的。这些不同的 URI 被指定在注解于方法之上的@RequestMapping 的value 属性中。但若这些请求具有相同的 URI 部分,则这些相同的 URI,可以被抽取到注解在类之上的@RequestMapping 的 value 属性中。此时的这个 URI 表示模块的名称。URI 的请求是相对于 Web 的根目录。换个角度说,要访问处理器的指定方法,必须要在方法指定 URI 之前加上处理器类前定义的模块名称。
3.Controller中方法上的请求URL不能有相同的,否则会在初始化的时候报错,导致项目无法启动
4.method:支持多种请求方式,如果不指定,默认都是可支持的:(method = RequestMethod.GET)
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
5.处理器方法携带的参数
处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序 可在方法内直接使用。
➢ HttpServletRequest
➢ HttpServletResponse
➢ HttpSession
6.设置字符集编码,防止POST请求乱码
<!--注册字符集过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
get提交默认的字符集是utf-8所以不会乱码,而post是ISO-8859-1,所以需要手动设置为utf-8才不会乱码
7.SpringMVC参数绑定
RequestMapping注解的各个属性值说明:
1.如果是直接提交过来的数据,只需要在方法中写上对应的参数名称就可以,springMVC在接收参数的使用会将数据类型转化为Controller方法中对应的数据类型,如果数据类型不对应,则会报错。
例如接收前端传递过来的 name值;URL?name=“value”;
@RequestMapping("likeBook")
public ModelAndView likeBook(String name){
ModelAndView mv = new ModelAndView();
List<Book> bookList = bookService.likeBook(name);
mv.addObject("bookList",bookList);
mv.setViewName("admin/bookManager");
return mv;
}
状态码:400,表示客户端在提交数据的过程中发生了错误。
2.校正请求参数名@RequestParam:
所谓校正请求参数名,是指若请求 URL 所携带的参数名称与处理方法中指定的参数名不相同时,则需在处理方法参数前,添加一个注解@RequestParam(“请求参数名”),指定请求 URL 所携带参数的名称。该注解是对处理器方法参数进行修饰的。value 属性指定请求参数的名称。
3.required 属性:
3.自定义对象参数映射(POST和Ajax用的较多)
@RequestMapping("updateBook")
public ModelAndView updateBook(Book book){
ModelAndView mv = new ModelAndView();
Boolean flag = bookService.updateBook(book);
mv.setViewName("admin/bookManager");
return mv;
}
4.路径变量:获取请求地址URL的值作为参数
- RESTful 风格:Representational State Transfer
REST最大的几个特点为:资源、统一接口、URI和无状态
-
资源:
所谓”资源”,就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。资源总要通过某种载体反应其内容,文本可以用txt格式表现,也可以用HTML格式、XML格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现;JSON是现在最常用的资源表示格式。
-
统一接口:
RESTful架构风格规定,数据的元操作,即CRUD(create, read, update和delete,即数据的增删查改)操作,分别对应于HTTP方法:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源,这样就统一了数据操作的接口,仅通过HTTP方法,就可以完成对数据的所有增删查改工作。
- URL:
可以用一个URI(统一资源定位符)指向资源,即每个URI都对应一个特定的资源。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或识别符。一般的,每个资源至少有一个URI与之对应,最典型的URI即URL
-
无状态:
所谓无状态的,即所有的资源,都可以通过URI定位,而且这个定位与其他资源无关,也不会因为其他资源的变化而改变。有状态和无状态的区别,举个简单的例子说明一下。如查询员工的工资,如果查询工资是需要登录系统,进入查询工资的页面,执行相关操作后,获取工资的多少,则这种情况是
有状态
的,因为查询工资的每一步操作都依赖于前一步操作,只要前置操作不成功,后续操作就无法执行;如果输入一个url即可得到指定员工的工资,则这种情况是无状态
的,因为获取工资不依赖于其他资源或状态,且这种情况下,员工工资是一个资源,由一个url与之对应,可以通过HTTP中的GET
方法得到资源,这是典型的RESTful风格
@GetMapping(value = "/json")
public @ResponseBody List<Hero> getUserInfo(){
List<User> userList = userService.findAllUser();
return userList;
}
8.路径请求路径映射问题
在jsp , html中使用的地址, 都是在前端页面中的地址,都是相对地址
地址分类:
1.绝对地址 , 带有协议名称的是绝对地址, http://www.baidu.com , ftp://202.122.23.1等
2.相对地址, 没有协议开头的, 例如 user/some.do , /user/some.do
第一种路径写法:
在你的页面中的,访问地址不加 “/”
当你的地址 没有斜杠开头,例如 user/some.do , 当你点击链接时, 访问地址是当前页面的地址加上链接的地址。
http://localhost:8080/工程名/ + user/some.do 相对路径加base标签
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Im9ccRys-1621776193289)(C:\Users\14823\AppData\Roaming\Typora\typora-user-images\image-20210520135541516.png)]
第一种写法的优化:以防止部署到服务器后是404
第二种路径写法:
在路径之前加上 / 的时候 就表示从 http://localhost:8080开始找所以没有带上工程名的时候会找不到地址,需要带上工程名地址才会正确.
使用el表达式在路径的前面动态的获取到工程名
9.请求转发和请求重定向
当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。
注意: 对于请求转发的页面,可以是WEB-INF中页面;而重定向的页面,是不能为WEB-INF中页的。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资
源的。
SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。现在可以使用简单的方式实现转发和重定向。
forward:表示转发,实现 request.getRequestDispatcher(“xx.jsp”).forward()
redirect:表示重定向,实现 response.sendRedirect(“xxx.jsp”)
1.请求转发:
forward:好处就是可以访问到WEB-INF下的的静态资源,当需要跳转的路径不是配置在视图解析器中的时候,可以使用forward进行灵活的跳转
2.请求重定向
SSM整合
1.mybatisConfig.xml:
<?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>
<!-- 配置全局属性 -->
<settings>
<!-- 使用jdbc的getGeneratedKeys获取数据库自增主键值 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 使用列别名替换列名 默认:true -->
<setting name="useColumnLabel" value="true" />
<!-- 开启驼峰命名转换:Table{create_time} -> Entity{createTime} -->
<setting name="mapUnderscoreToCamelCase" value="true" />
<!-- 输出mybatis 执行sql 语句的日志信息-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置别名-->
<typeAliases>
<!--name:实体类所在的包名(不是实体类的包名也可以)-->
<package name="com.books.bean"/>
</typeAliases>
<!-- sql mapper(sql映射文件)的位置-->
<mappers>
<!--
name:是包名, 这个包中的所有mapper.xml一次都能加载
使用package的要求:
1. mapper文件名称和dao接口名必须完全一样,包括大小写
2. mapper文件和dao接口必须在同一目录
-->
<package name="com.books.dao"/>
</mappers>
</configuration>
2.jdbc.properties:
jdbc.url=jdbc:mysql://localhost:3306/bookssm?serverTimezone=GMT%2B8
jdbc.drivers=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=2732195202
jdbc.maxActive=20
3.springMVC.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--springMVC配置文件, 声明controller和其它web相关的对象-->
<context:component-scan base-package="com.books.controller" />
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
<!--读写JSON的支持(Jackson) 解决:mvc:resources和@RequestMapping的冲突 -->
<mvc:annotation-driven />
<!-- 放行静态资源:static目录下的所有资源都可以进行访问 -->
<mvc:resources location="/static/" mapping="/static/**" />
<!-- 声明springMvc拦截器 -->
<mvc:interceptors>
<!-- 声明一个一个拦截器 -->
<mvc:interceptor>
<!-- 拦截的请求url地址 可以使用** 表示任意字符,文件或多级目录中的文件 -->
<mvc:mapping path="/**"/>
<!-- 放行的请求 -->
<mvc:exclude-mapping path="/user/**"/>
<mvc:exclude-mapping path="/book/**"/>
<!-- 声明拦截器对象 -->
<bean class="com.books.handler.LoginPermissions"> </bean>
</mvc:interceptor>
</mvc:interceptors>
<!-- 配置文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 配置字符编码集 -->
<property name="defaultEncoding" value="utf-8"> </property>
<!-- 配置文件上传大小 单位是字节 -1代表没有限制 maxUploadSizePerFile是限制每个上传文件的大小,而maxUploadSize是限制总的上传文件大小 -->
<property name="maxUploadSizePerFile" value="-1"> </property>
<!-- 不设置默认不限制总的上传文件大小,这里设置总的上传文件大小不超过10MB(10*1024*1024) -->
<property name="maxUploadSize" value="10485760"/>
</bean>
</beans>
4.applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- 支持注解的使用 -->
<context:annotation-config/>
<!--spring配置文件:-->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--声明数据源,连接数据库-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClassName" value="${jdbc.drivers}"/>
<!-- 最大连接池数量 -->
<property name="maxActive" value="${jdbc.maxActive}"/>
</bean>
<!--SqlSessionFactoryBean创建SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="configLocation" value="classpath:mybatis.xml" />
</bean>
<!--声明mybatis的扫描器,创建dao对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
<property name="basePackage" value="com.books.dao" />
</bean>
<!--声明service的注解@Service所在的包名位置-->
<context:component-scan base-package="com.books.service" />
<!--声明全局异常处理-->
<context:component-scan base-package="com.books.handler" />
<!--事务配置:注解的配置, aspectj的配置-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource"></property>
</bean>
<!-- 编写通知 -->
<tx:advice id="txActive" transaction-manager="transactionManager">
<!-- 通知 那些方法需要使用用 -->
<tx:attributes>
<!-- service包下以find开头的方法不需要使用事务 其余的方法一律支持事务,并且隔离级别为可重复读 -->
<tx:method name="find*" read-only="true" />
<tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" />
</tx:attributes>
</tx:advice>
<!-- 配置需要使用事务的地方 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="txPointcut" expression="execution(* com.books.service.*.*(..))" />
<!-- 将通知与切入点进入整合 -->
<aop:advisor advice-ref="txActive" pointcut-ref="txPointcut" />
</aop:config>
</beans>
5.测试环境:
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class BookServiceTest {
@Autowired
private BookService bookService;
@Test
public void myTest(){
// 需要测试的业务逻辑代码
}
}
6.pom.xml依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjpowernode</groupId>
<artifactId>bookSSM</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- 谷歌验证码-->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- jsp依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
<scope>provided</scope>
</dependency>
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- mybatis整合其他框架的依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!-- mysql驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!-- 德鲁伊连接池依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- jstl依赖 -->
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-spec</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.5</version>
</dependency>
<!-- spring集成测试依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--junit 测试依赖-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<!-- lombok 插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<!-- 文件上传的jar包 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
<exclusions>
<exclusion>
<artifactId>commons-io</artifactId>
<groupId>commons-io</groupId>
</exclusion>
</exclusions>
</dependency>
<!--aop 切面需要的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
7.拦截器类:
public class LoginPermissions implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取session中设置的用户名,如果为null就说明没有登录,先让其登录才可以进行访问,session的值在登录验证成功时设置
String username = (String)request.getSession().getAttribute("username");
if (username==null||username==""){
request.getRequestDispatcher("/WEB-INF/view/Login.jsp").forward(request,response);
return false;
}else {
// return true表示 放行该请求 false:表示拦截该请求,不允许进行访问
return true;
}
}
/**
* 特点:
* 1.在处理器方法之后执行的(MyController.doSome())
* 2.能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView中的数据和视图,可以影响到最后的执行结果。
* 3.主要是对原来的执行结果做二次修正,
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 特点:
* 1.在请求处理完成后执行的。框架中规定是当你的视图处理完成后,对视图执行了forward。就认为请求处理完成。
* 2.一般做资源回收工作的, 程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
8.全局异常处理:
**
* @ControllerAdvice : 控制器增强(也就是说给控制器类增加功能--异常处理功能)
* 位置:在类的上面。
* 特点:必须让框架知道这个注解所在的包名,需要在springmvc配置文件声明组件扫描器。
* 指定@ControllerAdvice所在的包名
*/
@ControllerAdvice
public class GlobalExceptionHandler {
//定义方法,处理发生的异常
/*
处理异常的方法和控制器方法的定义一样, 可以有多个参数,可以有ModelAndView,
String, void,对象类型的返回值
形参:Exception,表示Controller中抛出的异常对象。
通过形参可以获取发生的异常信息。
@ExceptionHandler(异常的class):表示异常的类型,当发生此类型异常时,
由当前方法处理
*/
@ExceptionHandler(value = NameException.class)
public ModelAndView doNameException(Exception exception){
//处理NameException的异常。
/*
异常发生处理逻辑:
1.需要把异常记录下来, 记录到数据库,日志文件。
记录日志发生的时间,哪个方法发生的,异常错误内容。
2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
3.给用户友好的提示。
*/
ModelAndView mv = new ModelAndView();
mv.setViewName("error");
return mv;
}
//处理AgeException
@ExceptionHandler(value = AgeException.class)
public ModelAndView doAgeException(Exception exception){
//处理AgeException的异常。
/*
异常发生处理逻辑:
1.需要把异常记录下来, 记录到数据库,日志文件。
记录日志发生的时间,哪个方法发生的,异常错误内容。
2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
3.给用户友好的提示。
*/
ModelAndView mv = new ModelAndView();
mv.setViewName("ageError");
return mv;
}
//处理其它异常, NameException, AgeException以外,不知类型的异常
@ExceptionHandler
public ModelAndView doOtherException(Exception exception){
//处理其它异常
ModelAndView mv = new ModelAndView();
mv.setViewName("defaultError");
return mv;
}
}
9.web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--谷歌验证码的使用-->
<servlet>
<servlet-name>KaptchaServlet</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>KaptchaServlet</servlet-name>
<url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>
<!--注册中央调度器-->
<servlet>
<servlet-name>MyWeb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MyWeb</servlet-name>
<!--
1.*.mvc:拦截所有请求地址中带有.mvc的请求
2./:拦截所有的请求资源(html.静态资源),jsp除外
3./*:拦截所有的资源,包括jsp
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--注册spring的监听器-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--注册字符集过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
p 切面需要的依赖 -->
org.springframework
spring-aspects
5.2.6.RELEASE
## 7.拦截器类:
```java
public class LoginPermissions implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取session中设置的用户名,如果为null就说明没有登录,先让其登录才可以进行访问,session的值在登录验证成功时设置
String username = (String)request.getSession().getAttribute("username");
if (username==null||username==""){
request.getRequestDispatcher("/WEB-INF/view/Login.jsp").forward(request,response);
return false;
}else {
// return true表示 放行该请求 false:表示拦截该请求,不允许进行访问
return true;
}
}
/**
* 特点:
* 1.在处理器方法之后执行的(MyController.doSome())
* 2.能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView中的数据和视图,可以影响到最后的执行结果。
* 3.主要是对原来的执行结果做二次修正,
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 特点:
* 1.在请求处理完成后执行的。框架中规定是当你的视图处理完成后,对视图执行了forward。就认为请求处理完成。
* 2.一般做资源回收工作的, 程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
8.全局异常处理:
**
* @ControllerAdvice : 控制器增强(也就是说给控制器类增加功能--异常处理功能)
* 位置:在类的上面。
* 特点:必须让框架知道这个注解所在的包名,需要在springmvc配置文件声明组件扫描器。
* 指定@ControllerAdvice所在的包名
*/
@ControllerAdvice
public class GlobalExceptionHandler {
//定义方法,处理发生的异常
/*
处理异常的方法和控制器方法的定义一样, 可以有多个参数,可以有ModelAndView,
String, void,对象类型的返回值
形参:Exception,表示Controller中抛出的异常对象。
通过形参可以获取发生的异常信息。
@ExceptionHandler(异常的class):表示异常的类型,当发生此类型异常时,
由当前方法处理
*/
@ExceptionHandler(value = NameException.class)
public ModelAndView doNameException(Exception exception){
//处理NameException的异常。
/*
异常发生处理逻辑:
1.需要把异常记录下来, 记录到数据库,日志文件。
记录日志发生的时间,哪个方法发生的,异常错误内容。
2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
3.给用户友好的提示。
*/
ModelAndView mv = new ModelAndView();
mv.setViewName("error");
return mv;
}
//处理AgeException
@ExceptionHandler(value = AgeException.class)
public ModelAndView doAgeException(Exception exception){
//处理AgeException的异常。
/*
异常发生处理逻辑:
1.需要把异常记录下来, 记录到数据库,日志文件。
记录日志发生的时间,哪个方法发生的,异常错误内容。
2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
3.给用户友好的提示。
*/
ModelAndView mv = new ModelAndView();
mv.setViewName("ageError");
return mv;
}
//处理其它异常, NameException, AgeException以外,不知类型的异常
@ExceptionHandler
public ModelAndView doOtherException(Exception exception){
//处理其它异常
ModelAndView mv = new ModelAndView();
mv.setViewName("defaultError");
return mv;
}
}
9.web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--谷歌验证码的使用-->
<servlet>
<servlet-name>KaptchaServlet</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>KaptchaServlet</servlet-name>
<url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>
<!--注册中央调度器-->
<servlet>
<servlet-name>MyWeb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MyWeb</servlet-name>
<!--
1.*.mvc:拦截所有请求地址中带有.mvc的请求
2./:拦截所有的请求资源(html.静态资源),jsp除外
3./*:拦截所有的资源,包括jsp
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--注册spring的监听器-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--注册字符集过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
resources目录结构:
转载:https://blog.csdn.net/m0_46188681/article/details/117200970