SSM
MyBatis
MyBatis的基本用法
pom.xml
导入依赖
-
log4j.properties
-
mapper.xml
写入映射- 命名空间:类似
java
包的名字,用于区分不同的映射文件 - 输入映射:
parameterType
输出映射:resultType
<mapper namespace="workerMapper"> <!-- 此映射配置文件 用来设计worker表对应的各种操作 --> <!-- 配置各种操作 --> <select id="selectByName" parameterType="java.lang.String" resultType="com.ls.bean.Worker"> select * from worker where wname = #{value} </select> <!-- 增 --> <insert id="add" parameterType="com.ls.bean.Worker"> insert into worker values(#{wid},#{wname},#{age},#{sex}) </insert> <!-- 删 --> <delete id="del" parameterType="java.lang.String"> delete from worker where wid = #{value} </delete> <!-- 改 --> <update id="upd" parameterType="com.ls.bean.Worker"> update worker set age = #{age} where wname = #{wname} </update> </mapper>
注: 除select标签外,其余标签没有resultType
- 命名空间:类似
-
sqlMapConfig.xml
中关联表的映射文件
- 补充:
- 单元测试里 @Before 标注的方法 会在每次@Test标注的方法 运行之前运行
@After 之后运行
Mybatis的mapper代理
步骤
- 建包,对照
- sqlMapConfiger.xml 配置
- 要实现Mapper接口 / dao接口 自动进行动态代理产生实现类
1.映射文件 需要 和 接口 放在相同的类路径下
2.映射文件 命名空间的值 跟 接口的路径名一样
3.映射文件 标签id 跟 接口的方法名一样
4.映射文件 标签 输入映射 跟 接口的方法入参类型一样
5.映射文件 标签 输出映射 跟 接口的方法返回值类型一样
uuid作主键
-
<mapper namespace="com.ls.mapper.WorkerDao"> <insert id="addWorker" parameterType="com.ls.bean.Worker"> /* 对于这样字符串做主键的 先执行selectkey里的操作,将结果设置到worker对象的wid里,再插入数据 */ <selectKey keyProperty="wid" keyColumn="wid" resultType="java.lang.String" order="BEFORE"> select uuid() </selectKey> insert into worker values (#{wid},#{wname},#{age},#{sex}) </insert> </mapper>
主键自增
当主键为自增型,想要查看插入后的主键
<insert id="addGoods" parameterType="com.ls.bean.Goods">
/* 在插入到数据库之后,将最后一次主键值 设置到 入参的goods对象的 goodsId 成员变量里 */
<selectKey keyProperty="goodsId" keyColumn="goodsId" resultType="java.lang.Integer" order="AFTER">
select last_insert_id()
</selectKey>
insert into goods(goodsName,shopPrice) values (#{goodsName},#{shopPrice})
</insert>
入参为多条件值
1.将多个条件值封装为一个对象
2.将多个条件值封装到一个Map
3.使用User类型传入角色和地址
<!-- vo -->
<select id="queryByRoleAndAddress" parameterType="com.ls.bean.QueryVo" resultMap="myUser">
select * from user where role_code = #{roleCode} and address = #{address}
</select>
<!-- 输入映射是map -->
<!-- #{roleCode} #{address} roleCode,address 就是Map里的一个键的名字 -->
<select id="queryByRoleAndAddress2"
parameterType="map" resultMap="myUser">
select * from user where role_code = #{roleCode} and address = #{address}
</select>
<!-- user -->
<select id="queryByRoleAndAddress3"
parameterType="com.ls.bean.User" resultMap="myUser">
select * from user where role_code = #{roleCode} and address = #{address}
</select>
下划线和驼峰的自动映射/手动映射
当数据库表里列名 和 java对象成员变量名 不一致 且不属于下划线和驼峰的自动映射时
resultType 输出映射 采用已经存在的java类型
自定义 输出映射 手动将列名和成员变量名 映射起来 采用resultMap
resultMap除了自定义输出映射之外,还能进行高级查询映射,1对1,1对多.
UserMapper.xml
<resultMap id="myUser" type="com.ls.bean.User">
<!-- 第一个主键列的映射 -->
<id property="userId" column="user_id" javaType="java.lang.Integer"/>
<!-- 其他列 -->
<result property="telephone" column="telphone" javaType="java.lang.String"/>
<!-- 其他的符合驼峰到下划线映射 或者 一致 -->
</resultMap>
<update id="updateTel" parameterType="com.ls.bean.User">
update user set telphone = #{telephone} where user_id = #{userId}
</update>
<select id="queryByID" parameterType="integer" resultMap="myUser">
select * from user where user_id = #{value}
</select>
${} 与 #{}
#{} 占位符
${} 字符串拼接
@Test
public void test050905() throws Exception{
GoodsMapper goodsMapper = sqlSession.getMapper(GoodsMapper.class);
List<Goods> goods = goodsMapper.queryByName2("'%X%'");//${} 识别 or 1=1
for (Goods good : goods) {
System.out.println(good);
}
}
@Test
public void test050904() throws Exception{
GoodsMapper goodsMapper = sqlSession.getMapper(GoodsMapper.class);
List<Goods> goods = goodsMapper.queryByName("%X%");//#{}
for (Goods good : goods) {
System.out.println(good);
}
}
入参为list
- foreach遍历入参list,每个值放在id变量里,遍历到拼接的sql语句里 自动去除最后一个id值后的" , "
- 输出映射,这里是泛型
<select id="queryByIds" parameterType="list" resultType="com.ls.bean.Goods">
select * from goods where goodsId
<foreach collection="list" item="id" open="in(" close=")" separator=",">
#{id}
</foreach>
</select>
根据入参里非空的属性值来修改某条记录
int update(User user);
<!--根据入参里非空的属性值来修改某条记录-->
<!-- set 标签自动去除最后一个逗号 -->
<update id="update" parameterType="com.ls.bean.User">
update user
<set>
<if test="userPass!=null and userPass!=''">
user_pass = #{userPass} ,
</if>
<if test="cname!=null and cname!=''">
cname = #{cname} ,
</if>
<if test="email!=null and email!=''">
email = #{email} ,
</if>
</set>
where user_id = #{userId}
</update>
@Test
public void test051002() throws Exception{
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUserPass("q1w2e3");
user.setCname("小球");
//user.setEmail("qqqqqq@qq.com");
user.setUserId(711);
userMapper.update(user);
}
查询
多条件查询
//多条件查询商品 条件有几个用户方不一定
List<Goods> queryByCondition(Goods condition);
<!-- 多条件查询 -->
<!-- where标签 自动去除第一个and关键字 -->
<select id="queryByCondition" parameterType="com.ls.bean.Goods" resultType="com.ls.bean.Goods">
select * from goods
<where>
<if test="goodsName!=null and goodsName!=''">
and goodsName like #{goodsName}
</if>
<if test="goodsStock!=null">
and goodsStock < #{goodsStock}
</if>
<if test="goodsStatus!=null">
and goodsStatus = #{goodsStatus}
</if>
</where>
</select>
1对多查询
collection
1对多 1对1 都是属于高级映射查询,输出映射必须是resultMap
User.java内
private List<Orders> ordersList;//一个用户下过的多个订单
//根据用户id查询用户数据 并 一同查询 他所下过的所有订单数据 -- 1对多查询
User queryUserAndOrders(Integer userId);
<!-- 高级映射 无论 成员变量名 和 列名 是否满足驼峰和下划线的映射,都必须手动写出来 -->
<resultMap id="user2" type="com.ls.bean.User">
<id property="userId" column="user_id" javaType="java.lang.Integer"/>
<result property="userName" column="user_name" javaType="java.lang.String"/>
<result property="roleCode" column="role_code" javaType="java.lang.String"/>
<result property="userPass" column="user_pass" javaType="java.lang.String"/>
<result property="cname" column="cname" javaType="java.lang.String"/>
<result property="isLogin" column="is_login" javaType="java.lang.String"/>
<result property="telephone" column="telphone" javaType="java.lang.String"/>
<result property="address" column="address" javaType="java.lang.String"/>
<result property="nation" column="nation" javaType="java.lang.String"/>
<result property="email" column="email" javaType="java.lang.String"/>
<result property="userImg" column="user_img" javaType="java.lang.String"/>
<result property="gender" column="gender" javaType="java.lang.String"/>
<!-- 此处为外键列:userId 和 user_id均可 -->
<!-- ofType写泛型 -->
<collection property="ordersList" column="userId" ofType="com.ls.bean.Orders">
<id property="orderId" column="orderId" javaType="java.lang.Integer"/>
<result property="userId" column="userId" javaType="java.lang.Integer"/>
<result property="price" column="price" javaType="java.lang.Double"/>
<result property="orderDate" column="orderDate" javaType="java.util.Date"/>
<result property="payment" column="payment" javaType="java.lang.Byte"/>
</collection>
</resultMap>
<select id="queryUserAndOrders" parameterType="java.lang.Integer" resultMap="user2">
select u.*,o.*
from user u left join orders o on u.user_id = o.userId
where u.user_id = #{value}
</select>
1对1查询
association
1对多 1对1 都是属于高级映射查询,输出映射必须是resultMap
//查询某个订单 一同查询 订单的下单用户
Orders queryOrdersAndUser(Integer orderId);
<resultMap id="orders2" type="com.ls.bean.Orders">
<id property="orderId" column="orderId" javaType="java.lang.Integer"/>
<result property="userId" column="userId" javaType="java.lang.Integer"/>
<result property="price" column="price" javaType="java.lang.Double"/>
<result property="orderDate" column="orderDate" javaType="java.util.Date"/>
<result property="payment" column="payment" javaType="java.lang.Byte"/>
<association property="user" column="userId" javaType="com.ls.bean.User">
<id property="userId" column="user_id" javaType="java.lang.Integer"/>
<result property="userName" column="user_name" javaType="java.lang.String"/>
<result property="roleCode" column="role_code" javaType="java.lang.String"/>
<result property="userPass" column="user_pass" javaType="java.lang.String"/>
<result property="cname" column="cname" javaType="java.lang.String"/>
<result property="isLogin" column="is_login" javaType="java.lang.String"/>
<result property="telephone" column="telphone" javaType="java.lang.String"/>
<result property="address" column="address" javaType="java.lang.String"/>
<result property="nation" column="nation" javaType="java.lang.String"/>
<result property="email" column="email" javaType="java.lang.String"/>
<result property="userImg" column="user_img" javaType="java.lang.String"/>
<result property="gender" column="gender" javaType="java.lang.String"/>
</association>
</resultMap>
<select id="queryOrdersAndUser" parameterType="java.lang.Integer" resultMap="orders2">
select o.*,u.* from orders o left join user u on o.userId = u.user_id
where o.orderId = #{value}
</select>
多对多查询
-
查询用户某个订单的商品
-
1个用户 -> 多个订单 1个订单 -> 多个订单信息 1个订单信息 -> 1件商品
-
字段
-
-
userMapper.java
-
//根据用户id查询用户数据 并 一同查询 他所购买过的所有商品数据 User queryUserAndGoods(Integer userId);
-
-
userMapper.xml
-
<resultMap id="user3" type="com.ls.bean.User"> <id property="userId" column="user_id" javaType="java.lang.Integer"/> <result property="userName" column="user_name" javaType="java.lang.String"/> <result property="roleCode" column="role_code" javaType="java.lang.String"/> <result property="userPass" column="user_pass" javaType="java.lang.String"/> <result property="cname" column="cname" javaType="java.lang.String"/> <result property="isLogin" column="is_login" javaType="java.lang.String"/> <result property="telephone" column="telphone" javaType="java.lang.String"/> <result property="address" column="address" javaType="java.lang.String"/> <result property="nation" column="nation" javaType="java.lang.String"/> <result property="email" column="email" javaType="java.lang.String"/> <result property="userImg" column="user_img" javaType="java.lang.String"/> <result property="gender" column="gender" javaType="java.lang.String"/> <!-- 此处userId 和 user_id均可 --> <!-- orderList --> <collection property="ordersList" column="userId" ofType="com.ls.bean.Orders"> <id property="orderId" column="orderId" javaType="java.lang.Integer"/> <result property="userId" column="userId" javaType="java.lang.Integer"/> <result property="price" column="price" javaType="java.lang.Double"/> <result property="orderDate" column="orderDate" javaType="java.util.Date"/> <result property="payment" column="payment" javaType="java.lang.Byte"/> <!-- orderDetailList --> <collection property="orderDetailList" column="orderId" ofType="com.ls.bean.OrderDetail"> <id property="detailId" column="detailId" javaType="java.lang.Integer"/> <result property="orderId" column="orderId" javaType="java.lang.Integer"/> <result property="goodsId" column="goodsId" javaType="java.lang.Integer"/> <result property="buyNum" column="buyNum" javaType="java.lang.Integer"/> <!-- goods --> <association property="goods" column="goodsId" javaType="com.ls.bean.Goods"> <id property="goodsId" column="goodsId" javaType="java.lang.Integer"/> <result property="goodsName" column="goodsName" javaType="java.lang.String"/> <!-- 为了节省篇幅 其他商品属性暂时不写,真正要查询时必须完整的写全 --> </association> </collection> </collection> </resultMap> <select id="queryUserAndGoods" parameterType="java.lang.Integer" resultMap="user3"> select * from user u left join orders o on u.user_id = o.userId left join order_detail od on o.orderId = od.orderId left join goods g on od.goodsId = g.goodsId where u.user_id = #{value} </select>
-
-
test
-
@Test public void test051005() throws Exception{ UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.queryUserAndGoods(704); System.out.println(user); List<Orders> ordersList = user.getOrdersList(); for (Orders orders : ordersList) { List<OrderDetail> detailList = orders.getOrderDetailList(); for (OrderDetail detail : detailList) { System.out.println(detail.getGoods()); } } }
-
-
结果
延迟加载(懒查询)
-
ordersMapper.java
-
询某个订单 一同查询 订单的下单
所以 将 两表连接查询 变为 2
那么查询订单 只需要单表查询 -
Orders queryOrdersAndUserLazy(Integer orderId);
-
-
ordersMapper.xml
-
<!-- 先查订单数据,并按照如下封装,关联了user属性时,再按照 com.ls.mapper.UserMapper.queryByID进行查询 --> <resultMap id="orders3" type="com.ls.bean.Orders"> <id property="orderId" column="orderId" javaType="java.lang.Integer"/> <result property="userId" column="userId" javaType="java.lang.Integer"/> <result property="price" column="price" javaType="java.lang.Double"/> <result property="orderDate" column="orderDate" javaType="java.util.Date"/> <result property="payment" column="payment" javaType="java.lang.Byte"/> <association property="user" column="userId" javaType="com.ls.bean.User" fetchType="lazy" select="com.ls.mapper.UserMapper.queryByID"> </association> </resultMap> <select id="queryOrdersAndUserLazy" parameterType="java.lang.Integer" resultMap="orders3"> select * from orders where orderId = #{value} </select>
-
-
test
-
@Test public void test051006() throws Exception{ OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class); Orders orders = ordersMapper.queryOrdersAndUserLazy(1003); System.out.println(orders.getOrderId()); System.out.println(orders.getOrderDate()); Thread.sleep(5000); System.out.println("5s以后..."); System.out.println(orders.getUser().getCname()); }
-
-
结果
缓存
一级缓存
mybatis一级缓存的作用域是同一个sqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。mybatis默认开启一级缓存。
@Test
public void test1() throws Exception{
InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
SqlSession sqlSession = factory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryByID(702);
System.out.println(user);
Thread.sleep(3000);
User user2 = userMapper.queryByID(702);
System.out.println(user2);
sqlSession.commit();
sqlSession.close();
}
二级缓存
二级缓存是sqlSessionFactory级别的
在mybatis中允许多个同一个工厂的SqlSession对象共享一个缓存区域,只不过这个缓存区域并不一定在内存中,也可能是存储硬盘空间内,这个共享区域就是mybatis的二级缓存。mybatis同样适用hashMap这种数据结构来存储二级缓存中保存的数据。 如下图所示:
从上图中可以看出,mybatis的二级缓存是根据mapper配置文件的namespace命名空间进行划分的,相同namespace的查询数据操作放在同一个缓存区中。即用户的查询操作的数据是放在UserMapper的缓存区中,订单查询操作的数据是放在OrderMapper的缓存区中。
如果两个用户的SqlSession会话都是执行同一个UserMapper接口中的方法,并且都去查询用户数据,每次查询都会先从缓存区中查找,如果找不到从数据库中查询,查询到的数据写入到二级缓存。
任何一个用户的SqlSession 执行insert、update、delete等操作commit提交后都会清空该mapper下的二级缓存区域。
在mybatis中二级缓存是默认关闭的,如果要开启mybatis的二级缓存,配置如下:
<setting name="cacheEnabled" value="true"/>
-
把要加入缓存的数据所在的类型实现序列化接口
二级缓存需要查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操作,注意如果存在父类、成员pojo都需要实现序列化接口。因为mybatis实现的二级缓存,数据并不一定存储在内存中,也有可能是其他位置,比如硬盘、网络空间等。
public class User implements Serializable { //省略... }
-
在UserMapper.xml中开启二级缓存, 加入cache标签
<mapper namespace="com.langsin.mapper.UserMapper"> <cache/> ...
-
测试:
@Test public void test2() throws Exception{ //一级缓存是sqlSession级别的,同一个sqlSession里 多次查询同样的数据 InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream); SqlSession sqlSession = factory.openSession(); //查第一次 UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.queryByID(702); System.out.println(user); sqlSession.commit(); sqlSession.close(); //一级缓存是sqlSession级别的,同一个sqlSession里 多次查询同样的数据 SqlSession sqlSession2 = factory.openSession();//------这里的factory要注意 //查第一次 UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class); //查第二次 日志里没有输出执行sql语句,查询了缓存 User user2 = mapper2.queryByID(702); System.out.println(user2); sqlSession2.commit(); sqlSession2.close(); }
在SqlSession会话中,如果会话没有结束,数据只会存储于一级缓存中,如果此SqlSession的会话结束并且此命名空间的mapper开启了二级缓存,这时数据才会写入到二级缓存中。
结果:
整合ehcache
- pom.xml导入依赖
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
</dependency>
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
-
添加ehcache的配置文件ehcache.xml到resources目录中
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <!-- 磁盘保存路径 --> <diskStore path="D:\ehcache" /> <defaultCache maxElementsInMemory="10000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache>
-
UserMapper.xml修改cache标签
- 开启mapper级别的缓存,即二级缓存,不同sqlSession使用此mapper,都共享二级缓存
- 不提供实现类 默认采用mybatis的内存形式二级缓存
- 提供mybatis整合ehcache工具时的实现类 二级缓存就采用 实现类设定的形式 在磁盘
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
- 测试运行: 使用二级缓存时的测试
D盘下发现缓存文件:
properties属性文件加载
-
加入的resources/jdbc.properties:
mysql.driver=com.mysql.cj.jdbc.Driver mysql.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=Asia/Shanghai mysql.user=root mysql.password=123456 oracle.driver=com.oracle.driver oracle.url=http://sdsdsdsd oracle.user=root oracle.password=123456
-
sqlMapConfig.xml:
按此顺序:
<!-- 以下配置不需掌握,和Spring整合后,以下配置将被废除 -->
<environments default="mysqlenv">
<environment id="mysqlenv">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.user}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
逆向工程
逆序工程的更多介绍:http://mybatis.org/generator/index.html
根据表结构, 产生javabean ,mapper接口 , mapper.xml. (简单的单表增删改查)
- 创建普通的java工程
- 加入generatorConfig.xml配置文件到项目路径下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
<!-- context元素用于指定生成一组对象的环境。targetRuntime:此属性用于指定生成的代码的运行时环境。MyBatis3:*这是默认值* -->
<context id="testTables" targetRuntime="MyBatis3Simple">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=Asia/Shanghai"
userId="root" password="123456">
<!-- connectionURL属性,防止生成不同数据库同名表的代码 -->
<property name="nullCatalogMeansCurrent" value="true"/>
</jdbcConnection>
<!-- 如使用oracle请参考如下 -->
<!-- <jdbcConnection driverClass="oracle.jdbc.driver.OracleDriver"
connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:orcl"
userId="senior1802" password="123">
</jdbcConnection> -->
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL
和 NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成PO类的位置 需要写绝对路径-->
<javaModelGenerator targetPackage="com.ls.bean"
targetProject=".\src">
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.ls.mapper"
targetProject=".\src">
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.ls.mapper" targetProject=".\src">
</javaClientGenerator>
<!-- 指定数据库表 -->
<table tableName="user" domainObjectName="User">
<columnOverride column="telphone" property="telephone" javaType="java.lang.String"></columnOverride>
</table>
<table tableName="orders" domainObjectName="Orders">
<columnOverride column="orderId" property="orderId" javaType="java.lang.Integer"></columnOverride>
<columnOverride column="userId" property="userId" javaType="java.lang.Integer"></columnOverride>
<!-- 指定某列到成员变量的生成规则 -->
<columnOverride column="price" property="price" javaType="java.lang.Double"></columnOverride>
<columnOverride column="orderDate" property="orderDate" javaType="java.util.Date"></columnOverride>
<columnOverride column="payment" property="payment" javaType="java.lang.Byte"></columnOverride>
</table>
<table tableName="goods" domainObjectName="Goods">
<columnOverride column="shopPrice" javaType="java.lang.Double"></columnOverride>
</table>
<table tableName="order_detail" domainObjectName="OrderDetail"></table>
<table tableName="worker" domainObjectName="Worker"></table>
</context>
</generatorConfiguration>
- 加入jar包到lib文件夹中, lib在项目下新建 并 指定为 lib
-
运行类main方法运行
public static void main(String[] args) throws Exception{ List<String> warnings = new ArrayList<String>(); boolean overwrite = true; File configFile = new File("generatorConfig.xml"); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(configFile); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); myBatisGenerator.generate(null); }
注解
- 简单的操作可以使用注解 就可以不写xml
- 复杂的操作 适合使用xml映射文件配置
WorkerDao:
@Select("select * from worker where age = #{value}")
List<Worker> queryByAge(int age);
@Update("update worker set age = #{age} where wname = #{wname}")
int updWorker(Worker worker);
测试:
@Test
public void test051202() throws Exception{
WorkerDao workerDao = sqlSession.getMapper(WorkerDao.class);
List<Worker> workers = workerDao.queryByAge(500);
workers.forEach(System.out::println);
Worker worker = new Worker();
worker.setAge(50);
worker.setWname("金毛狮王");
workerDao.updWorker(worker);
}
Spring
- 创建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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
-
Spring创建bean
-
使用无参构造创建bean对象
-
spring_config.xml
-
<!-- 演示spring容器功能 --> <!-- 1.使用无参构造创建对象 --> <bean class="com.ls.bean.Cat" id="cat"></bean> <bean class="com.ls.bean.Beauty" id="beauty"></bean>
-
-
MyTest.java
-
// 获取spring容器对象 ApplicationContext context = //基于类路径xml配置文件的 容器对象 实现类 new ClassPathXmlApplicationContext("spring_config.xml"); //通过bean的id 来获取 容器管理的bean对象 Cat cat = context.getBean("cat", Cat.class); Beauty beauty = context.getBean("beauty", Beauty.class); System.out.println(cat); System.out.println(beauty);
默认的 spring 容器管理的bean对象是单例的
容器默认用无参构造来创建对象 -
-
-
使用工厂方法创建bean对象
-
静态工厂方法 产生对象
-
public class StaticFlyFactory { public static Flyable creat(String choose){ if ("can".equals(choose)){ return new CanFly(); }else if ("cannot".equals(choose)){ return new CannotFly(); }else{ return null; } } }
-
<!-- 2.使用静态工厂方法创建对象 --> <bean class="com.ls.factory.StaticFlyFactory" id="scan" factory-method="creat"> <constructor-arg name="choose" value="can"/> </bean> <bean class="com.ls.factory.StaticFlyFactory" id="snot" factory-method="creat"> <constructor-arg name="choose" value="cannot"/> </bean>
-
-
非静态工厂方法 产生对象
-
public class FlyFactory { //非静态工厂方法 产生对象 public Flyable creat(String choose){ if ("1".equals(choose)){ return new CanFly(); }else if ("2".equals(choose)){ return new CannotFly(); }else{ return null; } } }
-
<!-- 3.使用非静态工厂方法创建对象,首先创建 工厂对象 --> <bean class="com.ls.factory.FlyFactory" id="flyFactory"></bean> <bean class="com.ls.bean.Flyable" id="can1" factory-bean="flyFactory" factory-method="creat"> <constructor-arg name="choose" value="1"/> </bean> <bean class="com.ls.bean.Flyable" id="cannot2" factory-bean="flyFactory" factory-method="creat"> <constructor-arg name="choose" value="2"/> </bean>
-
-
test
-
Flyable can = context.getBean("scan", Flyable.class); Flyable cannot = context.getBean("snot", Flyable.class); Flyable can1 = context.getBean("can1", Flyable.class); Flyable cannot2 = context.getBean("cannot2", Flyable.class); can.fly(); cannot.fly(); can1.fly(); cannot2.fly();
-
-
spring配置依赖
property注入/setter注入
-
<!-- 1.spring通过setter方法 完成依赖注入,也称property注入 --> <bean class="com.ls.bean.Dog" id="dog"> <property name="name" value="旺财"/> <property name="color" value="黄色"/> </bean> <bean class="com.ls.bean.Person" id="person"> <property name="name" value="张三"/> <property name="age" value="20"/> <property name="dog" ref="dog"/> <property name="hobbyArray"> <array value-type="java.lang.String"> <value>游泳</value> <value>登山</value> <value>钓鱼</value> </array> </property> <property name="scoreList"> <list value-type="java.lang.Integer"> <value>99</value> <value>100</value> <value>96</value> </list> </property> <property name="schoolSet"> <set value-type="java.lang.String"> <value>长清一小</value> <value>长清一中</value> </set> </property> <property name="sizeMap"> <map key-type="java.lang.String" value-type="java.lang.String"> <entry key="height" value="168cm"/> <entry key="weight" value="88kg"/> </map> </property> <property name="prop"> <props> <prop key="x1">v1</prop> <prop key="x2">v2</prop> </props> </property> </bean>
构造注入
-
<!-- 2.spring通过有参构造进行 属性值的注入 --> <bean class="com.ls.bean.Dog" id="dd"> <constructor-arg name="name" value="大黄"/> <constructor-arg name="color" value="yellow"/> </bean> <bean class="com.ls.bean.Person" id="pp"> <constructor-arg name="name" value="lucy"/> <constructor-arg name="age" value="22"/> <constructor-arg name="dog" ref="dd"/> </bean>
简化配置
- spring通过p c util 命名空间 来简化 property注入,构造注入和集合的写法
- util命名空间简化集合 集合复用 用的极少
- 配置文件
<?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:util="http://www.springframework.org/schema/util"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
https://www.springframework.org/schema/util/spring-util.xsd">
<!-- 3.spring通过p c util 命名空间 来简化 property注入,构造注入和集合的写法-->
<bean class="com.ls.bean.Dog" id="dog2" p:name="小狗2" p:color="灰色"></bean>
<bean class="com.ls.bean.Dog" id="dog3" c:name="小狗3" c:color="黑色"></bean>
<bean class="com.ls.bean.Person" id="person2" p:name="张小帅"></bean>
<bean class="com.ls.bean.Person" id="person3" c:name="李希" c:age="21" c:dog-ref="dog2"></bean>
<!-- util命名空间简化集合 集合复用 用的极少 -->
<util:list id="myscore" value-type="java.lang.Integer">
<value>100</value>
<value>99</value>
<value>98</value>
</util:list>
<bean class="com.ls.bean.Person" id="person4" c:name="jacky" c:scoreList-ref="myscore"></bean>
单例
- Spring默认的容器ApplicationContext管理的bean是单例.可以使用scope属性来指定proptotype等模式.
<bean class="com.ls.bean.Dog" id="dog2" p:name="小狗2" p:color="灰色"></bean>
<bean class="com.ls.bean.Dog" id="dog3" c:name="小狗3" c:color="黑色" scope="prototype"></bean>
- 测试单例
Dog dog2 = ac.getBean("dog2", Dog.class);
Dog dog22 = ac.getBean("dog2", Dog.class);
Dog dog3 = ac.getBean("dog3", Dog.class);
Dog dog33 = ac.getBean("dog3", Dog.class);
System.out.println(dog2 == dog22);
System.out.println(dog3 == dog33);
Spring容器
-
Spring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。
-
ApplicationContext默认会预初始化所有的singletion Bean,也可通过为bean元素指定**lazy-init=”true”**取消预初始化。
-
测试单例的预初始化
-
@Test public void test003() throws Exception{ //测试单例的预初始化 ApplicationContext ac = new ClassPathXmlApplicationContext("spring2.xml"); }//此时dog3非单例,dd dog2 dog3
-
-
取消单例的初始化
-
-
BeanFactory不会进行单例的初始化
- 没有任何输出
-
命名空间
自动装配
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName"><!-- 可以开启全局自动注入 -->
-
按类型自动装配(该类型只有一个)
<bean class="com.ls.bean.Cat" id="cc"> <property name="cname" value="tom"/> <property name="cage" value="2"/> </bean>
<bean class="com.ls.bean.Beauty" id="beauty" autowire="byType"> <property name="name" value="lucy"/> <property name="age" value="20"/> </bean>
-
按成员变量名和bean的id对应来自动装配
<bean class="com.ls.bean.Cat" id="cc">
<property name="cname" value="tom"/>
<property name="cage" value="2"/>
</bean>
<bean class="com.ls.bean.Cat" id="cat">
<property name="cname" value="kate"/>
<property name="cage" value="3"/>
</bean>
<bean class="com.ls.bean.Beauty" id="beauty" autowire="byName">
<property name="name" value="lucy"/>
<property name="age" value="20"/>
</bean>
-
spring配置文件里使用context命名空间下的标签来加载properties配置文件
<?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" 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"><!-- context命名空间 --> <!-- 使用context命名空间里的标签 加载 properties配置文件 --> <context:property-placeholder location="jdbc.properties"/>
<!-- 管理连接池 --> <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource"> <property name="driverClassName" value="${druid.driver}"/> <property name="username" value="${druid.user}"/> <property name="password" value="${druid.password}"/> <property name="url" value="${druid.url}"/> <property name="initialSize" value="2"/> <property name="maxActive" value="10"/> <property name="minIdle" value="3"/> <property name="maxWait" value="60000"/> </bean>
DruidDataSource dataSource = ac.getBean("dataSource", DruidDataSource.class); DruidPooledConnection connection = dataSource.getConnection(); System.out.println(connection);
-
管理JDBCTemplate对象
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"/> </bean>
JdbcTemplate template = ac.getBean("jdbcTemplate", JdbcTemplate.class); String sql = "select count(*) cs from goods"; Integer value = template.queryForObject(sql, Integer.class); System.out.println(value);
-
配置类
/** * 配置类 替代 配置文件 * 注解和方法 替代 配置文件里的标签 */ @Configuration //标注此注解的类 是个 配置类 public class MyConfig { @Value("lucy") private String name; @Value("22") private Integer age; @Bean public Beauty beauty(Cat cat){//默认的 方法名即容器管理的对象的id,该cat为id为cat的 //先按照类型匹配,若该类型有多个实现类,则按照名字匹配 Beauty beauty = new Beauty(); beauty.setName("萨达"); beauty.setAge(20); beauty.setCat(cat); return beauty; } @Bean(name = "girl")//起别名 public Beauty b2(Cat cc){//ByName Beauty beauty = new Beauty(name, age); beauty.setCat(cc); return beauty;//了解 } @Bean public Cat cat(){ Cat cat = new Cat(); cat.setCname("tomcat"); cat.setCage(1); return cat; } @Bean public Cat cc(){ Cat cat = new Cat(); cat.setCname("cc"); cat.setCage(2); return cat; } }
-
扫描五个注解的类称为容器里的组件_bean
@Repository //仓库 指持久层的组件 被容器管理的对象
public class UserDaoImpl implements UserDao {
@Override
public String getName(int id) {
//假装查数据库
if (id==1){
return "张三";
}else if (id==2){
return "李四";
}else return "王五";
}
}
@Service
public class UserServiceImpl implements UserService {
@Autowired //自动装配默认 先按照类型装配,然后尝试从多个里再按名字进行装配
//最后如果名字也没有符合的 则 报错 希望1个但找到2个
//@Qualifier("userDaoImpl2") 两个以上可以使用限定符
@Resource(name = "userDaoImpl")//两个以上可以使用resource
private UserDao userDao;//IOC DI
@Override
public String getName(int id) {
return userDao.getName(id);
}
}
//@Controller("uc") //默认的将类名首字母小写 作为bean的id值
@Controller
public class UserController {
@Autowired
private UserService userService;
public void show(){
String name = userService.getName(1);
System.out.println(name);
String name1 = userService.getName(2);
System.out.println(name1);
}
}
@Configuration //标注此注解的类 是个 配置类
//扫描配置 扫描哪些个包里带有五大注解的类 成为 容器管理的对象 即组件
@ComponentScan(basePackages = {"com.ls"})
public class MyConfig {
....
}
@Test
public void test003() throws Exception{
ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
//获取容器中定义的所有组件/bean 的名字 id/name
String[] names = ac.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
配置类和配置文件一起使用
配置类 中引用 配置文件
<?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"
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">
<bean class="com.ls.bean.Dog" id="bigDog">
<property name="name" value="大狗"/>
<property name="color" value="灰色"/>
</bean>
@Configuration
@ComponentScan(basePackages = {"com.ls"})
@ImportResource(locations = {"spring_b.xml"})
public class MyConfig {
@Test
public void test006() throws Exception{
ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
String[] names = ac.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
配置文件为主 引用 配置类
<?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"
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">
<bean class="com.ls.bean.Dog" id="bigDog">
<property name="name" value="大狗"/>
<property name="color" value="灰色"/>
</bean>
<!-- 以配置文件为主 扫描5个注解组件,其中就有配置类 -->
<context:component-scan base-package="com.ls"/>
</beans>
@Configuration
//@ComponentScan(basePackages = {"com.ls"})
public class MyConfig {
结果:
@Test
public void test006() throws Exception{
ApplicationContext ac = new ClassPathXmlApplicationContext("spring_b.xml");
String[] names = ac.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
Spring-test
spring-test包 里面增强了单元测试功能
-
导入依赖
-
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.18</version> </dependency>
-
属性locations配置多个xml文件地址
-
//单元测试时 使用spring增强过的单元测试工具 @RunWith(value = SpringRunner.class) //容器配置 @ContextConfiguration(locations = "classpath:spring_b.xml") public class SpTest { @Autowired ApplicationContext ac; @Test public void test001() throws Exception{ Dog bigDog = ac.getBean("bigDog", Dog.class); System.out.println(bigDog); } }
-
-
属性classes配置多个配置类的类型
-
@RunWith(value = SpringRunner.class) @ContextConfiguration(classes = MyConfig.class) public class SpTest { @Autowired ApplicationContext ac; @Test public void test001() throws Exception{ Dog bigDog = ac.getBean("bigDog", Dog.class); System.out.println(bigDog); } }
-
-
AOP
相关概念
- 通知/增强: 公共业务逻辑 , 对主体业务逻辑进行的增强. 通知通常是方法形式.
- 前置通知: 主体业务逻辑执行前
- 后置通知: 主体业务逻辑执行完,无论有无异常
- 后置通知之返回值之后:主体业务逻辑返回了返回值之后
- 异常通知: 主体业务逻辑执行发生了异常
- 环绕通知: 主体业务逻辑执行前,执行后
- 连接点: 指主体业务逻辑所在的**方法**,可以在这些方法内添加 增强/通知.
- 通常指业务层的方法
- 切入点: 连接点满足条件时,加入了增强/通知,就变为了切入点.
- 目标对象: 通常指业务层的对象. 被代理对象
- 代理对象: 框架通过JDK动态代理 或 cglib代理产生的目标对象的代理对象.(业务层的替身)
- 织入: 把通知添加到切入点里,形成代理对象的过程. 这是一个动作.
- 切面: 通知/增强 与 切入点的关系! 通知和切入点的统称. 这里强调 通知和切入点的关系. 比如: 前置还是后置等. 这里强调的是 我们要 配置!!
通知和切入点的关系:前置,后置,环绕,异常,返回值之后
前置/后置/异常通知
-
导入依赖
-
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.18</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.18</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.18</version> </dependency><!--!!!!!!!!!!!!!!!!--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency> </dependencies>
-
-
通知
-
增强/通知 以方法形式存在的
-
public class MyAdvice { //业务层方法执行之前执行 public void logTime(){ String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); System.out.println("Time: "+format); } //正常执行业务层方法之后 public void after(){ System.out.println("----执行之后才调用----"); } //业务层方法异常之后执行 public void th(){ System.out.println("===出现异常之后执行==="); } }
-
-
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"> <!-- 扫描添加了5个注解的组件 --> <context:component-scan base-package="com.ls"/> <!-- 通知类 --> <bean class="com.ls.util.MyAdvice" id="myAdvice"/> <!-- aop配置 --> <aop:config> <!-- 切面配置,切面就是通知和切入点 --> <aop:aspect ref="myAdvice"><!-- ref:关联已有的通知类对象 --> <!-- 切入点表达式,在业务层的所有连接点方法里 找 符合的 进行增强,之后成为切入点方法 --> <!-- 第一个* 表示 任何返回值类型 --> <!-- com.ls.service..* 表示:com.ls.service包及其所有子包里的任何类 --> <!-- add() 表示add方法 --> <!-- (..) 表示 任何参数 --> <aop:pointcut id="pt1" expression="execution(* com.ls.service..*.add(..))"/> <aop:pointcut id="pt2" expression="execution(* com.ls.service..*.update*(..))"/> <aop:pointcut id="pt3" expression="execution(* com.ls.service..*.del*(..))"/> <!-- 通知和切入点的关系:前置,后置,环绕,异常,返回值之后 --> <!-- 按照pt1表达式来找到 切入点方法,织入logTime方法,前置织入 --> <aop:before method="logTime" pointcut-ref="pt1"/> <aop:before method="logTime" pointcut-ref="pt2"/> <aop:after method="after" pointcut-ref="pt2"/> <!-- 异常通知 --> <aop:after-throwing method="th" pointcut-ref="pt3"/> </aop:aspect> </aop:config> </beans>
-
-
测试类
-
@RunWith(SpringRunner.class) @ContextConfiguration(locations = {"classpath:spring.xml"}) public class MyTest { @Autowired private ApplicationContext ac; @Test public void test() throws Exception{ UserController userController = ac.getBean("userController", UserController.class); // userController.add(); userController.del(); // userController.update(); // userController.query(); } }
-
-
结果
环绕通知
环绕通知 可以自己控制 被代理对象:目标对象 的切入点方法在何时执行,可以偷换切入点方法的入参和返回值
环绕通知必须提供一个参数 表示切入点对象:ProceedingJoinPointer
MyAdvice.java
@Around("ptt3()")
public Object myAround(ProceedingJoinPoint pt){
//环绕通知里的目标方法执行前:
System.out.println("环绕通知 之前置通知:");
//执行目标方法
try {
Object result = pt.proceed();
//环绕通知里的目标方法执行后
System.out.println("环绕通知 之后置通知:");
} catch (Throwable throwable) {
//环绕通知里的目标方法发生异常时
System.out.println("环绕通知 执行目标方法时有异常了!");
}
return null;
}
<!-- aop配置 -->
<aop:config>
<!-- 切面配置,切面就是通知和切入点 -->
<aop:aspect ref="myAdvice"><!-- ref:关联已有的通知类对象 -->
<aop:pointcut id="pt4" expression="execution(* com.ls.service..*.query*(..))"/>
<!-- 环绕通知 -->
<aop:around method="myAround" pointcut-ref="pt4"/>
</aop:aspect>
</aop:config>
@Test
public void test() throws Exception{
UserController userController = ac.getBean("userController", UserController.class);
userController.query();
}
注解配置
xml里的 切点 标签 改为方法+注解
在通知的方法前加注解
-
spring2.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"> <!-- 扫描添加了5个注解的组件 --> <context:component-scan base-package="com.ls"/> <!-- 开启aop的注解 --> <aop:aspectj-autoproxy/> </beans>
-
-
通知类
-
@Component //成为spring容器里的一个组件 @Aspect //切面类,在此配置切面 public class MyAdvice { //xml里的 切点 标签 改为方法+注解 @Pointcut("execution(* com.ls.service..*.*(..))") public void ptt(){ } @Pointcut("execution(* com.ls.service..*.update(..))") public void ptt2(){ } @Pointcut("execution(* com.ls.service..*.del(..))") public void ptt3(){ } //业务层方法执行之前执行 @Before("ptt()") public void logTime(){//通知 String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); System.out.println("Time: "+format); } //正常执行业务层方法之后 @After("ptt2()") public void after(){ System.out.println("----执行之后才调用----"); } //业务层方法异常之后执行 public void th(){ System.out.println("===出现异常之后执行==="); @Around("ptt3()") public Object myAround(ProceedingJoinPoint pt){ //环绕通知里的目标方法执行前: System.out.println("环绕通知 之前置通知:"); //执行目标方法 try { Object result = pt.proceed(); //环绕通知里的目标方法执行后 System.out.println("环绕通知 之后置通知:"); } catch (Throwable throwable) { //环绕通知里的目标方法发生异常时 System.out.println("环绕通知 执行目标方法时有异常了!"); } return null; } }
-
-
test
-
@RunWith(SpringRunner.class) @ContextConfiguration(locations = {"classpath:spring2.xml"}) public class MyTest2 { @Autowired private ApplicationContext ac; @Test public void test001() throws Exception{ UserController userController = ac.getBean("userController", UserController.class); userController.add(); // userController.del(); userController.update(); userController.query(); } }
-
XML配置的AOP事务
给业务层方法添加事务操作,事务操作就是通知/增强
事务操作: 异常回滚 手动提交等, 这些增强在spring中默认提供了一个类,事务通知类
事务属性: 隔离级别 传播级别
JDBC
-
导入依赖
-
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.13</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.20</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.18</version> </dependency>
-
jdbc.properties
-
spring3.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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 扫描添加了5个注解的组件 --> <context:component-scan base-package="com.ls"/> <!-- 关联properties属性配置文件 --> <context:property-placeholder location="jdbc.properties"/> <!-- 管理连接池对象 --> <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource"> <property name="driverClassName" value="${druid.driver}"/> <property name="username" value="${druid.user}"/> <property name="password" value="${druid.password}"/> <property name="url" value="${druid.url}"/> </bean> <!-- 管理JDBCTemplate对象 --> <bean class="org.springframework.jdbc.core.JdbcTemplate" id="template"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 基于连接池的事务管理对象 --> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 事务属性配置 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 配置insert开头的方法的事务属性,隔离,传播 --> <tx:method name="insert*" isolation="REPEATABLE_READ" propagation="REQUIRED"/> <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED"/> <tx:method name="complex*" isolation="REPEATABLE_READ" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- AOP面向切面编程 将 事务通知 切入到 业务层进行增强 --> <aop:config> <aop:pointcut id="pt1" expression="execution(* com.ls.service..*.*(..))"/> <!-- 事务助手(切面) 通知 切点 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/> </aop:config> </beans>
-
-
-
测试
-
表
-
操作
-
-
结果
- 数据库表中插入失败也失败,说明事务开启
注解版springAOP事务配置
-
spring4.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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 扫描添加了5个注解的组件 --> <context:component-scan base-package="com.ls"/> <!-- 关联properties属性配置文件 --> <context:property-placeholder location="jdbc.properties"/> <!-- 管理连接池对象 --> <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource"> <property name="driverClassName" value="${druid.driver}"/> <property name="username" value="${druid.user}"/> <property name="password" value="${druid.password}"/> <property name="url" value="${druid.url}"/> </bean> <!-- 管理JDBCTemplate对象 --> <bean class="org.springframework.jdbc.core.JdbcTemplate" id="template"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 基于连接池的事务管理对象 --> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 开启注解版事务配置 --> <tx:annotation-driven/> </beans>
-
-
service
-
@Service @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED) //当前业务层类 里的所有方法都可添加事务 默认事务配置 public class WorkerServiceImpl implements WorkerService { @Autowired private WorkerDao workerDao; //单独给某方法配置事务属性 @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRES_NEW) @Override public int insertWorker(Worker worker) { return workerDao.insertWorker(worker); } @Override public int updateWid(String wid, String oldId) { return workerDao.updateWid(wid,oldId); } @Override public void complexA() { // 第一操作 新增一个 Worker w1 = new Worker("B12","阿萨德",23,"男"); int i = insertWorker(w1); System.out.println("第一操作结果: "+i); //第二操作 将刚才新增的那个id值修改为其他的 int i1 = updateWid("Rt001", "B12"); System.out.println("第二操作结果: "+i1); } }
-
测试成功(记得更换读的配置文件)
-
SM整合
-
导入依赖
-
<!-- 依赖 --> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.18</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.18</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.18</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.18</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.13</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.20</version> </dependency> <!-- mybatis log4j mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> </dependencies>
-
-
添加配置文件,建包
-
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> <!-- MyBatis的全局参数设置,基本用来进行MyBatis的优化处理 --> <settings> <setting name="logImpl" value="LOG4J"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> <setting name="cacheEnabled" value="true"/> <!-- 开启数据库字段名下划线到java对象成员变量名驼峰映射 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!-- 类型别名 --> <typeAliases> <package name="com.ls.bean"/> </typeAliases> <!-- 连接池,事务管理等 都在spring里配置 --> </configuration>
-
-
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" xmlns:xontext="http://www.springframework.org/schema/context" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 扫描标注了5个注解组件 --> <context:component-scan base-package="com.ls"/> <!-- properties文件 --> <context:property-placeholder location="jdbc.properties"/> <!-- 连接池对象 --> <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource"> <property name="url" value="${mysql.url}"/> <property name="username" value="${mysql.user}"/> <property name="password" value="${mysql.password}"/> <property name="driverClassName" value="${mysql.driver}"/> </bean> <!-- 事务管理器 --> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 开启注解版的AOP --> <aop:aspectj-autoproxy/> <!-- 事务注解驱动 --> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- mybatis的sqlSessionFactoryBean --> <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- mybatis的配置文件 --> <property name="configLocation" value="mybatis.xml"/> </bean> <!-- 扫描@Mapper注解的一个类 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="configurer"> <property name="basePackage" value="com.ls.mapper"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/> </bean> </beans>
-
-
UserMapper.java
-
//@Repository spring中 5个组件注解之一,语义:持久层 @Mapper //spring 和 mybatis整合之后 有一个组件MapperScanConfig 专门扫描@Mapper注解 public interface UserMapper { List<User> queryByCondition(User user); int updateUser(User user); int changePass(User u1); }
-
UserMapper.xml 完善查询
-
-
UserServiceImpl.java
-
@Service @Transactional(isolation = Isolation.REPEATABLE_READ) public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Transactional(propagation = Propagation.SUPPORTS) @Override public List<User> queryByCondition(User user) { return userMapper.queryByCondition(user); } @Transactional(propagation = Propagation.REQUIRED) @Override public int updateUser(User user) { return userMapper.updateUser(user); } @Transactional(propagation = Propagation.REQUIRED) @Override public void showTrans(User u1, User u2) { int rows = userMapper.changePass(u1); System.out.println(rows); rows = userMapper.changePass(u2); System.out.println(rows); } }
-
-
test
-
public void showChangePass(){ User u1 = new User(); u1.setUserId(708); u1.setUserName("milla"); u1.setUserPass("1234"); User u2 = new User(); u2.setUserId(711); u2.setUserName("milla"); u2.setUserPass("1234"); userService.showTrans(u1,u2); }
-
-
数据库未更改成功, 事务演示成功
-
-
SpringMVC
handler方法 / controller方法 的返回值类型
1.ModelAndView类型 数据和视图信息 是根本操作
2.String类型
A: 返回逻辑视图名,“abc” 经过视图解析器的解析封装
B: 转发
① 转发到其它handler方法
② 直接转发到jsp页面, 此用法不经过视图解析器,页面路径写全
C: 重定向
① 重定向到其它handler方法
② 重定向到jsp页面 不能是WEB-INF里面, 此用法不经过视图解析器,页面路径写全
③ 重定向到其它服务器
3.void
可以在handler方法入参位置,自由的写req,resp,session这些WEB AIP,传统的web用法
可以处理ajax请求 使用resp返回数据
4.@ResponseBody + 任意类型返回值 都能返回 json数据
1. 返回ModelAndView
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<h3><a href="${pageContext.request.contextPath}/beauty/testModelAndView">testModelAndView</a></h3>
@GetMapping("/testModelAndView")
public ModelAndView testModelAndView(){
ModelAndView mv = new ModelAndView();
mv.setViewName("show");
String[] names = {"张三","李四","王五"};
mv.addObject("names",names);
return mv;
}
2. 返回String
1.返回逻辑视图名
- pom.xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
-
springconfig
-
Converter<S,T> 是 springMVC里的转换器接口,框架内已有多个 应用常用个类型转型的实现类 但没有 将 yyyy-MM-dd 格式 字符串 转为 Date 类型的转换器 需要自定义 字符串到日期类型转换器实现类 完成yyyy-MM-dd 格式 字符串 转为 Date 类型
-
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 注解组件扫描 -->
<context:component-scan base-package="com.ls"/>
<!-- 处理器映射器: 找,处理器适配器: 执行 -->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
<!-- 视图解析器:根据路径信息找到jsp页面 -->
<!-- 比如:/WEB-INF/templates/abc.jsp abc叫做逻辑视图名 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 转换器工厂bean 用于添加自定义的转换器 --> 要在注解里配置
<bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.ls.config.MyStringToDateConverter"/>
</set>
</property>
</bean>
</beans>
-
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <welcome-file-list> <welcome-file>/index.jsp</welcome-file> </welcome-file-list> <!-- 配置springMVC的 前端控制器:一个中心 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 当tomcat初始化前端控制器时 需要将spring容器对象也初始化 --> <init-param> <!-- spring容器配置,配置文件路径 --> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring_*.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> <!-- springMVC的框架里, 前端控制器访问路径 / 指: 除了jsp之外的所有请求 --> <!-- tomcat里默认servlet 访问路径也是 / , 用于处理静态资源,此时就失效了 --> </servlet-mapping> <!-- 配置字符编码过滤器 springMVC里提供的 --> <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> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
-
准备前端页面index,点击链接访问handler
@Controller
@RequestMapping("/beauty")
public class BeautyController {
//请求中的参数绑定: 请求参数自动封装到handler方法里的入参位置
//@RequestMapping(path = "/testBasic",method = RequestMethod.GET)
@GetMapping("/testBasic") //等于上面一句的效果
public String testBasicGet(String bname,Integer bage){
System.out.println(bname+"~"+bage);
return "abc";
}
@PostMapping("/testBasic")
public String testBasicPost(Beauty beauty){
//请求参数名字 和 方法入参对象的成员变量名一致,自动封装到对象的成员变量里
System.out.println(beauty);
return "abc";
}
@PostMapping("/testBasicCat")
public String testBasicCat(Beauty beauty){
System.out.println(beauty);
return "abc";
}
}
- handler返回abc页面
2.转发
- 转发到其它handler方法
转发如何携带数据 ModelAndView ModelMap Model 自由的定义到入参部分
<h3><a href="${pageContext.request.contextPath}/beauty/testDispatcher">testDispatcher</a></h3>
@GetMapping("testDispatcher")
public String testDispatcher(ModelMap map){
map.addAttribute("msg","演示携带数据");
return "forward:/hello";
}
@Controller
public class HelloHandler {
@RequestMapping("/hello")
public String helloShow(){
System.out.println("helloShow执行了!");
return "abc";
}
}
- 直接转发到jsp页面, 此用法不经过视图解析器,页面路径写全
<h3><a href="${pageContext.request.contextPath}/beauty/testDispatcherJsp">testDispatcherJsp</a></h3>
@RequestMapping("testDispatcherJsp")
public String testDispatcherJsp(Model model){
model.addAttribute("msg","转到jsp");
//转发不经过视图解析器,需要自己写全页面路径
return "forward:/WEB-INF/templates/abc.jsp";
}
3.重定向
- 重定向到其它handler方法
- 重定向无法使用 ModelAndView ModelMap Model 共享数据,因为是两次请求
- mvc框架自动的将 上述数据 拼接到 要重定向的路径 之后 ? 开头 键值对形式
- 重定向路径开头的**/也代表当前应用**,跟web阶段的重定向(localhost:8080)不同的
<h3><a href="${pageContext.request.contextPath}/beauty/testRedirect">testRedirect</a></h3>
@RequestMapping("/testRedirect")
public String testRedirect(ModelMap map){
map.addAttribute("msg","重定向到handler方法演示");
return "redirect:/hello";
}
- 重定向到jsp页面
<h3><a href="${pageContext.request.contextPath}/beauty/testRedirectJsp">testRedirectJsp</a></h3>
@GetMapping("/testRedirectJsp")
public String testRedirectJsp(ModelMap map){
map.addAttribute("msg","重定向到jsp页面演示");
return "redirect:/plain.jsp";
}
页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>plain</title>
</head>
<body>
<h2>这是一个普通的jsp页面! msg = ${msg} / 请求参数msg = ${param.msg}</h2>
</body>
</html>
- 重定向其他服务器,需要将url写全
return "redirect:http://www.jd.com"
3.返回void
可以在handler方法入参位置,自由的写req,resp,session这些WEB AIP,传统的web用法
可以处理ajax请求 使用resp返回数据
- testServlet
<form action="${pageContext.request.contextPath}/beauty/testServlet" method="post">
<h3>testBasic - POST</h3>
美女名字:<input type="text" name="bname" value="大岩子"/><br/>
美女年龄:<input type="text" name="bage" value="20"/><br/>
<input type="submit"/>
</form>
<hr style="border: 1px dashed purple;" />
@PostMapping("/testServlet")
public void testServlet(HttpServletRequest req, HttpServletResponse resp, HttpSession session,Beauty beauty)throws Exception{
System.out.println(beauty);
req.setAttribute("beauty",beauty);
session.setAttribute("bea",beauty);
req.getRequestDispatcher("/WEB-INF/templates/abc.jsp").forward(req,resp);
}
- testAjax
导入jq
<!-- 开启静态资源处理器 作用如同: tomcat里的默认servlet -->
<!-- 前端控制器 DispatcherServlet 访问路径为 / ,除了jsp之外的资源,包括静态资源 -->
<!-- 我们希望 前端控制器 只处理handler动态资源,所以要配置默认servlet来处理 js,css等静态资源 -->
<mvc:default-servlet-handler/>
<h2><button id="btn">点击使用ajax</button></h2>
<script>
$("#btn").click(function () {
$.ajax({
url:'${pageContext.request.contextPath}/beauty/testAjax',
type:'post',
async:'true',
data:{"bname":'李小花',"bage":22},
dataType:'text',
success: function (backData) {
console.log(backData);
}
})
})
</script>
@PostMapping("/testAjax")
public void testAjax(HttpServletRequest req, HttpServletResponse resp,Beauty beauty)throws Exception{
System.out.println(beauty);
resp.getWriter().write("ajax ok!");
}
4.返回 json数据
@ResponseBody + 任意类型返回值 都能返回 json数据
<h3><button id="testResponseBody">testResponseBody</button></h3>
<table id="tab" style="width: 50%;" border="1px">
<tr>
<td>美女名字</td>
<td>美女年龄</td>
<td>美女猫名字</td>
<td>美女猫年龄</td>
</tr>
</table>
<script>
$("#testResponseBody").click(function () {
$.ajax({
url:'${pageContext.request.contextPath}/beauty/testResponseBody',
type: 'post',
dataType: 'json',
success:function (backData) {
console.log(backData);
for (let i = 0; i < backData.length; i++) {
let newTr = $("<tr></tr>");
let td1 = $("<td></td>");td1.text(backData[i].bname);
let td2 = $("<td></td>");td2.text(backData[i].bage)
let td3 = $("<td></td>");td3.text(backData[i].cat.cname);
let td4 = $("<td></td>");td4.text(backData[i].cat.cage);
newTr.append(td1);
newTr.append(td2);
newTr.append(td3);
newTr.append(td4);
$("#tab").append(newTr);
}
}
})
})
</script>
@PostMapping("/testResponseBody")
@ResponseBody
public List<Beauty> testResponseBody(){
Cat c1 = new Cat();c1.setCname("tom");c1.setCage(2);
Beauty b1 = new Beauty();b1.setCat(c1);b1.setBname("福克斯");b1.setBage(28);
Cat c2 = new Cat();c2.setCname("cat");c2.setCage(3);
Beauty b2 = new Beauty();b2.setCat(c2);b2.setBname("露西");b2.setBage(28);
List<Beauty> beautyList = new ArrayList<>();
beautyList.add(b1);
beautyList.add(b2);
return beautyList;
}
参数绑定
1.请求参数名 和 handler方法入参名 不一致
<a href="${pageContext.request.contextPath}/beauty/testRequestParam?name=王五&age=20">testRequestParam</a>
@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam(name = "name") String bname,
@RequestParam(name = "age") Integer bage,
@RequestParam(name = "sex",required = false,defaultValue = "女") String sex){
System.out.println(bname + "/" + bage+ "/" + sex);
return "abc";
}
2.@CookieValue 获取请求中的cookie
<h3><a href="${pageContext.request.contextPath}/beauty/testCookieValue">testCookieValue</a></h3>
@CookieValue 和 @RequestParam 一样 有四个属性
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue(name = "JSESSIONID") String sessionID){
System.out.println(sessionID);
return "abc";
}
3.@ModelAttribute
@ModelAttribute 添加到方法上,在handler其它方法执行前,必须先执行
目的:
这样的方法 设置一些数据 连同 用户端的请求参数 一并封装到 其他方法的入参位置
提前准备一些客户端没有的数据
<form action="${pageContext.request.contextPath}/beauty/testModelAttribute" method="post">
<h3>testModelAttribute</h3>
美女名字:<input type="text" name="bname" value="李冰冰"/><br/>
美女年龄:<input type="text" name="bage" value="38"/><br/>
<input type="submit"/>
</form>
<hr style="border: 1px dashed greenyellow;"/>
@ModelAttribute
public Beauty init(){
Beauty beauty = new Beauty();
Cat cat = new Cat();
cat.setCage(3);
cat.setCname("tomcat");
beauty.setCat(cat);
String[] hobby = {"看书","写作","游泳","cosplay"};
beauty.setHobby(hobby);
return beauty;
}
@PostMapping("/testModelAttribute")
public String testModelAttribute(Beauty beauty){
System.out.println(beauty);
return "abc";
}
Beauty(bname=李冰冰, bage=38, cat=Cat(cname=tomcat, cage=3), hobby=[看书, 写作, 游泳, cosplay], dogs=null, edu=null, birthday=null)
上述带有 @ModelAttribute的方法具有返回值, 也可以不写返回值,但需要将数据添加到Model或ModelMap中,在需要参数绑定的方法的入参部分 使用@ModelAttribute来指定.
@ModelAttribute
public void init(ModelMap map) {
Beauty beauty = new Beauty();
Cat cat = new Cat();
cat.setCage(2);
cat.setCname("mimi");
beauty.setCat(cat);
String[] hobby = {"蓝球","足球","羽毛球","cosplay"};
beauty.setHobby(hobby);
map.addAttribute("bb",beauty);
}
@PostMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute(name = "bb") Beauty beauty){
System.out.println(beauty);
return "abc";
}
Beauty(bname=李冰冰, bage=38, cat=Cat(cname=mimi, cage=2), hobby=[蓝球, 足球, 羽毛球, cosplay], dogs=null, edu=null, birthday=null)
4.@SessionAttribute
将session域中的属性 绑定到Handler方法的入参中
<%-- 向session域中 设置属性 --%>
<c:set scope="session" var="ns" value="老杨"/>
<h3>
<a href="${pageContext.request.contextPath}/beauty/testSessionAttribute">testSessionAttribute</a>
</h3>
@RequestMapping("/testSessionAttribute")
public String testSessionAttribute(@SessionAttribute(name = "ns") String name,
HttpSession session){
//springMVC建议 去除 web API
System.out.println(name);
System.out.println(session.getAttribute("ns"));
return "abc";
}
5.@SessionAttributes
默认的Model , ModelMap 中存放的属性都在请求域里, 如果想把属性设置到session域中而不使用ServletAPI,那么需要使用和这个注解. 此注解用在类上.
-
index.jsp
<h3><a href="${pageContext.request.contextPath}/beauty/testSessionAttributes">testSessionAttributes</a></h3>
-
Controller
@Controller
@RequestMapping("/beauty")
@SessionAttributes(names = {"beauty"})
//向请求域中设置名为beauty的域属性时,一并设置到 会话域里, 为了不让使用webAPI
public class BeautyController {
@RequestMapping("/testSessionAttributes")
public String testSessionAttributes(ModelMap map){
Beauty beauty = new Beauty();
beauty.setBname("lucy");
beauty.setBage(20);
//默认的 ModelMap Model ModelAndView 管理的属性 设置到请求域里
map.addAttribute("beauty",beauty);
Beauty b2 = new Beauty();
b2.setBname("TomSen");
b2.setBage(22);
map.addAttribute("b2",b2);
return "dataShow";
}
}
- dataShow.jsp
<html>
<head>
<title>Title</title>
</head>
<body>
<p>请求域里的beauty = ${requestScope.beauty.bname} - ${requestScope.beauty.bage}</p>
<p>会话域里的beauty = ${sessionScope.beauty.bname} - ${sessionScope.beauty.bage}</p>
<p>请求域里的b2 = ${requestScope.b2.bname} - ${requestScope.b2.bage}</p>
<p>会话域里的b2 = ${sessionScope.b2.bname} - ${sessionScope.b2.bage}</p>
</body>
</html>
6.@RequestHeader
获取请求中的头信息 用法跟@RequestParam , @CookieValue 类似 .
-
index.jsp
<h3><a href="${pageContext.request.contextPath}/beauty/testRequestHEAD">testRequestHEAD</a></h3>
-
handler
@GetMapping("/testRequestHEAD")
public String testRequestHEAD(@RequestHeader(name = "host") String host,
@RequestHeader Map<String,Object> map){
System.out.println(host);
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key + "=" + map.get(key));
}
return "abc";
}
- 输出:
7.@RequestAttribute
是给 Handler方法的 入参 添加的注解.
从request域中 拿出指定名字的属性值 给 方法入参 封装进来.
@GetMapping("/testRequestHEAD")
public String testRequestHEAD(@RequestHeader(name = "host") String host,
@RequestHeader Map<String,Object> map,
Model model){
System.out.println(host);
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key + "=" + map.get(key));
}
model.addAttribute("car","小汽车");
//转发到/testAttr 里
return "forward:/beauty/testAttr"; //"forward:testAttr"
}
@RequestMapping("/testAttr")
public String testAttr(@RequestAttribute(name = "car") String attrData,
Model model){
System.out.println(attrData);
model.addAttribute("msg","过年了要!");
return "abc";
}
restful风格
RESTful(即Representational State Transfer的缩写)其实是一个开发理念,是对http的很好的诠释。Representational State Transfer的含义就是“表现层的状态转化”。
RestFul 表现层状态转换. 是一种处理web层请求的风格
- URL简介 原本?后的key,value形式的请求参数 不在使用键值对,直接把值变为URL的一部分 称为:路径参数
- 请求方式 使用四种
get: 执行查询请求
post:执行新增请求
put: 执行修改请求
delete: 执行删除请求
为了可以把post请求 变为 put 和 delete ,配置过滤器
此过滤器 会根据表单隐藏域_method 的值 来转换post请求
3、除post get外要设置成json格式的返回值
- web.xml
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
<init-param>
<param-name>methodParam</param-name>
<param-value>mym</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.index.jsp
3.handler
@RequestParam 是 处理传统的键值对形式的 请求参数 到 Handler方法入参的变量 相关
Restful风格里 请求参数 变为了路径参数
@Controller
public class RestfulHandler {
@GetMapping("/beautyOpreation/{age}.html") //{age} 是路径参数 {}为占位符
public String testRestfulGet(@PathVariable(name = "age") Integer bage){
System.out.println("进行查询: 目标年龄 : " + bage);
return "abc";
}
@PostMapping("/beautyOpreation")
public String testRestfulPost(Beauty beauty){
System.out.println(beauty);
return "abc";
}
@PutMapping("/beautyOpreation/{id}/{color}")
@ResponseBody
public String testRestfulPut(Beauty b1,
@PathVariable String id,
@PathVariable String color){
System.out.println("PUT请求");
System.out.println(b1);
System.out.println(id);
System.out.println("color = " + color);
return "put okk!";
}
@DeleteMapping("/beautyOpreation")
@ResponseBody
public String testRestfulDelete(Beauty b1){
System.out.println(b1);
System.out.println("删除操作");
return "del okk!";
}
}
矩阵变量: @MatrixVariable 可以将restful风格请求路径里的矩阵参数 绑定到 handler方法入参中. 矩阵变量需要开启配置
- SpringMVC默认未开始 对 矩阵变量 解析和封装为handler方法入参, 需要在配置文件中开启 矩阵变量的使用 .
<mvc:annotation-driven enable-matrix-variables="true"/>
- web.xml
<h3><a href="${pageContext.request.contextPath}/item/97;name=lucy/98;name=lee/908;name=ss">多矩阵变量同名</a></h3>
<form action="${pageContext.request.contextPath}/user/97;name=zhangsasn;age=1,2,3" method="post">
<input type="text" name="myparam" value="修改"/>
<input type="hidden" name="mym" value="PUT">
<input type="submit" value="提交">
</form>
解决 字符编码过滤器 和 hiddenMethod过滤器 restful乱码:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>
text/plain;charset=utf-8
</value>
<value>
text/html;charset=utf-8
</value>
<value>
application/json;charset=utf-8
</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
上传文件
- 静态资源服务器准备
-
导入commons-fileupload jersy
-
spring.xml
<!-- 配置文件上传解析器,来解析文件上传数据 自动封装到handler方法入参部分的 MultipartFile -->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<!-- 每个上传的文件限制大小为100Mb -->
<property name="maxUploadSizePerFile" value="104857600"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
- index.jsp
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
美女的名字:<input type="text" name="bname" value="小梅"/><br/>
美女的年龄:<input type="text" name="bage" value="25"/><br/>
上传的文件:<input type="file" name="myfile" id="myfile"/><br/>
<img id="myimg" width="50px" height="50px" style="display: none" />
<input type="submit"/>
</form>
<script type="text/javascript">
$("#myfile").change(function (){
var file = $("#myfile").get(0).files[0];// js对象的files属性
var srcStr = URL.createObjectURL(file);
$("#myimg").attr("style", "display:inline;");
$("#myimg").attr("src", srcStr);
});
</script>
- handler
@PostMapping("/upload")
public String upload(Beauty beauty, MultipartFile myfile, ModelMap map) throws IOException {
System.out.println(beauty);
System.out.println(myfile.getName());
System.out.println(myfile.getContentType());
System.out.println(myfile.getContentType());
System.out.println(myfile.getOriginalFilename());
System.out.println(myfile.getSize() + "byte");
String uuid = UUID.randomUUID().toString().replace("-","");
String saveFileName = uuid + myfile.getOriginalFilename();
String saveURL = "http://localhost:7070/mvc/" + saveFileName;
Client client = new Client();
WebResource webResource = client.resource(saveURL);
webResource.put(myfile.getInputStream());
map.addAttribute("msg","上传成功! 路径: " + saveURL);
return "abc";
}
自定义异常及处理器
- 自定义异常
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class CustomException extends Exception{
private String code;
private String message;
}
- 自定义异常处理器
@Component//自动扫描为 spring组件/bean
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
ModelAndView mv = new ModelAndView();
mv.setViewName("error");
//ex 是发生的异常对象
if (ex instanceof CustomerException){
CustomerException ee = (CustomerException) ex;
mv.addObject("errMsg",ee.getCode() + "~" + ee.getMessage());
}else {
mv.addObject("errMsg",ex.getMessage());
}
return mv;
}
}
- index.jsp
<form action="${pageContext.request.contextPath}/testException" method="post">
<h4>值: 0出现未知异常,1出现Customer异常,其他正常</h4>
测试的参数number:<input type="text" name="number"/><br/>
<input type="submit"/>
</form>
- handler
@PostMapping("/testException")
public String testException(String number, Model model)throws Exception{
//测试: 0抛出非custom异常 , 1抛出custom异常 , 其他正常
if ("0".equals(number)){
throw new ArrayIndexOutOfBoundsException("数组下标越界异常!");
}else if ("1".equals(number)){
throw new CustomerException("999","xxx异常!");
}else {
model.addAttribute("msg","正常情况无异常!");
}
return "abc";
}
拦截器
SpringMVC的拦截器类似于JSP/Servlet中的Filter过滤器,用于对处理器handler进行预处理和后处理。
在SpringMVC中提供了一个HandlerInterceptor接口,实现了该接口的实现类即为SpringMVC的拦截器.
public class MyInterceptorA implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptorA 预处理,放行!");
//response.sendRedirect(request.getContextPath()+"/index.jsp");
return true;//false 拦截 true 放行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptorA 后处理!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptorA 在页面渲染之后!");
}
}
public class MyInterceptorB implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptorB 预处理,放行!");
return true;//false 拦截 true 放行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptorB 后处理!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptorB 在页面渲染之后!");
}
}
preHandle方法是在Handler方法执行前执行, 根据返回值来决定是否向后传递请求和响应.返回false表示拦截.
postHandle方法是在Handler方法执行后并返回ModelAndView之前执行.
afterCompletion方法是在ModelAndView渲染完毕后执行.
演示:
- index.jsp
<h3><a href="${pageContext.request.contextPath}/testInterceptor">testInterceptor</a></h3>
- 拦截器A和B
public class MyInterceptorA implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptorA 预处理,放行!");
//response.sendRedirect(request.getContextPath()+"/index.jsp");
return true;//false 拦截 true 放行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptorA 后处理!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptorA 在页面渲染之后!");
}
}
public class MyInterceptorB implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptorB 预处理,放行!");
return true;//false 拦截 true 放行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptorB 后处理!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptorB 在页面渲染之后!");
}
}
preHandle方法是在Handler方法执行前执行, 根据返回值来决定是否向后传递请求和响应.返回false表示拦截.
postHandle方法是在Handler方法执行后并返回ModelAndView之前执行.
afterCompletion方法是在ModelAndView渲染完毕后执行.
- Spring配置文件中配置拦截器
<!-- 配置拦截器的 拦截路径 -->
<!-- 配置拦截器bean -->
<bean class="com.ls.interceptor.MyInterceptorA" id="a"></bean>
<bean class="com.ls.interceptor.MyInterceptorB" id="b"></bean>
<!-- 配置拦截器的 拦截路径等 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截路径 -->
<mvc:mapping path="/testInterceptor"/>
<mvc:mapping path="/a/b/c/**"/>
<mvc:mapping path="/plain.jsp"/>
<ref bean="a"/>
</mvc:interceptor>
<mvc:interceptor>
<!-- 拦截路径 -->
<mvc:mapping path="/**"/>
<!-- 排除路径 -->
<mvc:exclude-mapping path="/index.jsp"/>
<mvc:exclude-mapping path="/plain.jsp"/>
<mvc:exclude-mapping path="/js/**"/>
<mvc:exclude-mapping path="/css/**"/>
<mvc:exclude-mapping path="/img/**"/>
<ref bean="b"/>
</mvc:interceptor>
</mvc:interceptors>
注: /test/** 是指访问路径为 /test之下任意路径.
- 执行结果
总结:
preHandle方法按拦截器的配置顺序调用
postHandler方法按拦截器的配置顺序逆向调用
afterCompletion方法按拦截器的配置顺序逆向调用
postHandler方法在拦截器链内的所有拦截器返回为true才调用
afterCompletion方法在拦截器的preHandle方法返回为true才调用
我们可以使用拦截器完成web阶段过滤器能完成的功能:比如 登录验证等.
SpringMVC的校验
SpringMVC在 Controller / Handler层的校验 仅仅是 校验 handler方法入参封装的合法性。
- 添加Hibernate的Validator的校验框架的jar包
这里必须使用 6版本
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
- 配置messageSource错误消息Bean
<!-- 可以读取 验证信息文件的 bean -->
<bean class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
id="messageSource">
<property name="defaultEncoding" value="UTF-8"/>
<property name="basenames">
<array value-type="java.lang.String">
<value>classpath:beautyValidator</value>
</array>
</property>
</bean>
- 将hibernate校验器类型 配置给 校验器工厂bean
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"
id="localValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<property name="validationMessageSource" ref="messageSource"/>
</bean>
- Bean中的validatorMessage.properties
# 验证handler方法入参为Beauty类型时,各个成员变量的格式或数据的报错信息
beauty.bname.blank=美女名字不能为空,长度大于0
beauty.bage.min=美女必须是个成年人
beauty.birthday.past=美女生日必须是个过去时间
- 将配好的校验器注入到处理器适配器的驱动Bean中
<!-- 处理器映射器 , 处理器适配器 -->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean" validator="localValidatorFactoryBean"/>
- 在bean的成员变量上添加校验规则
@Data
@ToString
@NoArgsConstructor
public class Beauty {
@NotBlank(message = "{beauty.bname.blank}")
private String bname;
@Min(message = "{beauty.bage.min}",value = 18)
private Integer bage;
private Cat cat;
private String[] hobby;//爱好
private List<Dog> dogs;
private LinkedHashMap<String,String> edu;//学历
@Past(message = "{beauty.birthday.past}")
private Date birthday;
private String birthStr;
}
- handler
@PostMapping("/testValidator")
public String testValidator(@Validated Beauty beauty, BindingResult result, Model model){
// @Validated 开启bean数据的校验
//BindingResult 校验的结果,必须紧跟在@Validated的后面
if (result.hasErrors()){//如果校验结果里 有错误信息
List<ObjectError> allErrors = result.getAllErrors();//获取所有错误信息
for (ObjectError allError : allErrors) {
System.out.println("objectName"+allError.getObjectName());
System.out.println("code" + allError.getCode());
System.out.println("defaultMessage"+allError.getDefaultMessage());
System.out.println("------------------------------------");
}
model.addAttribute("msg","有错误!");
model.addAttribute("allErrors",allErrors);
beauty.setBirthStr(new SimpleDateFormat("yyyy-MM-dd").format(beauty.getBirthday()));
model.addAttribute("beauty",beauty);
return "start";
}
System.out.println(beauty);
return "abc";
}
SSM整合
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.14</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.20</version>
</dependency>
<!-- 文件上传,单元测试等 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
配置文件
<?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>
<!-- MyBatis的全局参数设置,基本用来进行MyBatis的优化处理 -->
<settings>
<setting name="logImpl" value="LOG4J"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="cacheEnabled" value="true"/>
<!-- 开启数据库字段名下划线到java对象成员变量名驼峰映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 输入映射和输出映射时 类型别名配置 -->
<typeAliases>
<package name="com.ls.bean"/>
</typeAliases>
</configuration>
Mybatis
- log4j
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
- sqlMapConfigure(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>
<!-- MyBatis的全局参数设置,基本用来进行MyBatis的优化处理 -->
<settings>
<setting name="logImpl" value="LOG4J"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="cacheEnabled" value="true"/>
<!-- 开启数据库字段名下划线到java对象成员变量名驼峰映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 输入映射和输出映射时 类型别名配置 -->
<typeAliases>
<package name="com.ls.bean"/>
</typeAliases>
</configuration>
Spring
<?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>
<!-- MyBatis的全局参数设置,基本用来进行MyBatis的优化处理 -->
<settings>
<setting name="logImpl" value="LOG4J"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="cacheEnabled" value="true"/>
<!-- 开启数据库字段名下划线到java对象成员变量名驼峰映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 输入映射和输出映射时 类型别名配置 -->
<typeAliases>
<package name="com.ls.bean"/>
</typeAliases>
</configuration>
SpringMVC
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 只扫描控制层 @Controller -->
<context:component-scan base-package="com.ls.controller"/>
<!-- 处理器映射器 和 处理器适配器 的注解驱动 -->
<!-- 自定义转换器服务bean 开启矩阵变量 bean数据校验 -->
<mvc:annotation-driven/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 静态资源 -->
<mvc:default-servlet-handler/>
<!-- 其他配置如 转换器,拦截器,自定义异常解析器,文件上传... -->
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<welcome-file-list>
<welcome-file>/index.jsp</welcome-file>
</welcome-file-list>
<!-- 前端控制器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring_*.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!-- 除了jsp之外的所有请求 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 字符编码过滤器 -->
<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>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Restful风格请求方法过滤器 -->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
<init-param>
<param-name>methodParam</param-name>
<param-value>mym</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置了 字符编码过滤器 和 restful风格过滤器之后,restful请求里还可能乱码 -->
</web-app>
测试
- index.jsp
<a href="${pageContext.request.contextPath}/show/36">GET</a>
<form action="${pageContext.request.contextPath}/show" method="post">
最大年龄: <input type="number" name="age"><br/>
<button type="submit">提交</button>
</form>
- WorkerController
@Controller
public class WorkerController {
@Autowired
private WorkerService workerService;
@RequestMapping("/show")
public String show(Integer age, Model model){
List<Worker> workers = workerService.queryByMaxAge(age);
model.addAttribute("workers",workers);
return "show";
}
@GetMapping("/show/{age}")
public String show2(@PathVariable Integer age, Model model){
List<Worker> workers = workerService.queryByMaxAge(age);
model.addAttribute("workers",workers);
return "show";
}
}
- show.jsp
<c:forEach items="${workers}" var="w">
<p>${w.wname} - ${w.age} - ${w.sex}</p>
</c:forEach>