尚硅谷雷丰阳大神的Spring、Spring MVC、MyBatis课程
eclipse安装spring插件地址
https://spring.io/tools3/sts/all
①选择对应版本插件包下载
②安装步骤
1.help=》install new software
2.add=》archive
3.选择四个带有springIDE字样的选项
eclipse导入spring框架包
1.导入包到lib文件夹下,Build Path
spring framework 下载地址https://repo.spring.io/release/org/springframework/spring/
Spring核心容器
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
Spring运行的时候依赖一个日志包,如果没有就报错
commons-logging-1.2.jar 下载地址http://commons.apache.org/proper/commons-logging/download_logging.cgi
2.src创建配置文件
spring配置文件中,集合了spring的ioc容器管理的所有组件(会员清单)
创建一个Spring Bean Configuration File(Spring的bean配置文件)
3.注册一个对象,spring会自动创建这个对象
一个Bean标签可以注册一个组件(对象、类),class写要注册的组件的全类名,id,这个对象的唯一标示
junit test 测试
①创建Person.java
package com.demo; public class Person { private String lastName; private Integer age; private String email; private String gender; @Override public String toString() { return this.lastName+this.email+this.gender+this.age; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } } |
②src下添加bean
<?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"> <bean id="person1" class="com.demo.Person"> <property name="lastName" value="张三"></property> <property name="age" value="11"></property> <property name="email" value="412198579@qq.com"></property> <property name="gender" value="男"></property> </bean> </beans> |
③创建单元测试文件
package com.demo; import static org.junit.Assert.*; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class IOCTest { @Test public void test() { // 创建 Spring 的 IOC 容器 // ApplicationContext代表IOC容器 ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml"); // 从 IOC 容器中获取 bean 的实例 Person bean = (Person) ioc.getBean("person1"); System.out.println(bean); } } |
报错java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory at org.springframework.jdbc.support.JdbcAccessor.<init>(JdbcAccessor.java:43) at org.springframework.jdbc.core.JdbcTemplate.<init>(JdbcTemplate.java:164) at DemoJDBCTemplate.Templatedemo1.main(Templatedemo1.java:13) Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496) ... 3 more |
解决方案如下:导入 导入commons-logging-1.2.jar辅助类包即可。
+++++++++++++++++++
报错java.lang.Exception: No tests found matching
java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=test2], {ExactMatcher:fDisplayName=test2(cn.amumu.spring.test.shiro.ShiroTest12)], {LeadingIdentifierMatcher:fClassName=cn.amumu.spring.test.shiro.ShiroTest12,fLeadingIdentifier=test2]] from org.junit.internal.requests.ClassRequest@33f88ab at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:37) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createFilteredTest(JUnit4TestLoader.java:77) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:68) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:43) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:444) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) |
解决方案如下:使用的junit版本为4.12,使用的spring包是4.0.0,将spring包更换为4.1.2
+++++++++++++++++++++++++++++
使用有参构造器创建javabean
Person.java
public Person(String lastName, Integer age, String email, String gender) { this.lastName = lastName; this.age = age; this.email = email; this.gender = gender; } |
ioc.xml
<bean id="person03" class="com.bean.Person"> <constructor-arg name="lastName" value="王二麻子"></constructor-arg> <constructor-arg name="age" value="11"></constructor-arg> <constructor-arg name="email" value="haha@qq.com"></constructor-arg> <constructor-arg name="gender" value="男士"></constructor-arg> </bean> |
注意:可以省略name属性,但必须严格按照构造参数顺序赋值,但是推荐使用带name属性的方式最好。
+++++++++++++++++++
通过p空间命名为bean赋值
beans属性就多了p属性 xmlns:p(xmlns:p=”http://www.springframework.org/schema/p”)
<bean id="person04" class="com.bean.Person" p:lastName="小小" p:age="11" p:email="222@qq.com" p:gender="女"> </bean> |
++++++++++++++
为bean属性赋值复杂元素
ioc.xml
<bean class="com.bean.Person" name="person01"> <property name="lastName" value="null"> </property> </bean> <bean class="com.bean.Person" name="person02"> <property name="lastName"> <null></null> </property> |
单元测试
@Test public void test5() { ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml"); Person bean = ioc.getBean("person01",Person.class); System.out.println(bean.getLastName() == null); // 输出false } @Test public void test6() { ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml"); Person bean = ioc.getBean("person02",Person.class); System.out.println(bean.getLastName() == null); // 输出true } |
+++++++++++++++++++++++++++++
ref引用外部的值(外部bean)
ioc2.xml
<bean class="com.bean.Person" id="person03"> <property name="lastName" value="xiaoli"></property> <property name="car" ref="car01"></property> //引用外部类 </bean> <bean class="com.bean.Car" id="car01"> <property name="carName" value="大众汽车"></property> </bean> |
test.java
public void test7() { ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml"); Person bean = ioc.getBean("person03",Person.class); Car car = bean.getCar(); car.setCarName("宝马汽车"); System.out.println(car.getCarName()); } |
+++++++++++++++++++++++++++++
配置xml文件改写可以达到同样的方式(内部bean)
<bean class="com.bean.Person" id="person03"> <property name="lastName" value="xiaoli"></property> <property name="car"> <bean id="car01" class="com.bean.Car"> <property name="carName" value="劳斯莱斯汽车"></property> </bean> </property> </bean> |
+++++++++++++++++++++
为list属性赋值
Person.java
private List<Book> books; |
ioc2.xml
<bean class="com.bean.Person" id="person08"> <property name="lastName" value="xiaoli"></property> <property name="books"> <list> <bean class="com.bean.Book" id="book001" p:bookName="零基础学jva" p:author="明日科技" ></bean> <bean class="com.bean.Book" id="book002" p:bookName="零基础学PHP" p:author="明日科技" ></bean> <ref bean="book003"></ref> </list> </property> </bean> <bean class="com.bean.Book" id="book003"> <property name="bookName" value="零基础学C#" ></property> <property name="author" value="明日科技"></property> </bean> |
Test.java
public void test8() { ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml"); Person bean = ioc.getBean("person08",Person.class); System.out.println(bean.getBooks()); } |
++++++++++++++++++
为map属性赋值
Person.java
private Map<String,Object> maps; public Map<String, Object> getMaps() { return maps; } public void setMaps(Map<String, Object> maps) { this.maps = maps; } |
ioc.xml
<bean class="com.bean.Person" id="person08"> <property name="lastName" value="xiaoli"></property> <property name="maps"> <map> <entry key="key01" value="喜洋洋"></entry> <entry key="key02" value="灰太狼"></entry> <entry key="key03" value-ref="book003"></entry> </map> </property> </bean> <bean class="com.bean.Book" id="book003"> <property name="bookName" value="零基础学C#" ></property> <property name="author" value="明日科技"></property> </bean> |
Test.java
public void test8() { ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml"); Person bean = ioc.getBean("person08",Person.class); System.out.println(bean.getMaps()); } |
++++++++++++++++++
为Properties属性赋值
Person.java
private Properties properties; public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } |
ioc2.xml
<bean class="com.bean.Person" id="person08"> <property name="properties"> <props> <prop key="username">小强</prop> <prop key="age">18</prop> </props> </property> </bean> |
Test.java
@Test public void test8() { ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml"); Person bean = ioc.getBean("person08",Person.class); System.out.println(bean.getProperties()); } |
++++++++++++++++++++++++++++++
为util命名空间创建一个可以外部引用的集合
ioc2.xml
<bean class="com.bean.Person" id="person010"> <property name="maps" ref="map01"></property> </bean> <util:map id="map01"> <entry key="key01" value="孙悟空"></entry> <entry key="key02" value="猪八戒灰太狼"></entry> <entry key="key03" value-ref="book003"></entry> </util:map> |
Test.java
@Test public void test10() { ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml"); Person bean = ioc.getBean("person010",Person.class); Map maps = (Map<String,Object>)(bean.getMaps()); System.out.println(maps); } |
++++++++++++++++++++++++++++++
级联属性赋值(为属性的属性赋值)
ioc2.xml
<bean class="com.bean.Person" id="person011"> <property name="car" ref="car01"></property> <property name="car.carName" value="吉利帝豪"></property> </bean> <bean class="com.bean.Car" id="car01"> <property name="carName" value="雪佛兰赛欧"></property> </bean> |
Test.java
@Test public void test11() { ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc2.xml"); Person bean = ioc.getBean("person011",Person.class); System.out.println(bean.getCar().getCarName()); } |
++++++++++++++++++++++++++++++
通过bean继承,达到重用的效果,通过bean的parent属性
<bean id="person02" class="com.bean.Person"> <property name="lastName" value="李四"></property> <property name="age" value="11"></property> <property name="email" value="110@qq.com"></property> <property name="gender" value="女"></property> </bean> <bean id="person05" class="com.bean.Person" parent="person02"> <property name="lastName" value="王二麻子"></property> </bean> |
++++++++++++++++++++++++++++++
通过abstract调用一个模板bean,如果直接使用会报错,只能被继承
<bean id="person06" class="com.bean.Person" abstract="true"> <property name="lastName" value="凌凌漆"></property> <property name="age" value="11"></property> <property name="email" value="110@qq.com"></property> <property name="gender" value="女"></property> </bean> |
++++++++++++++++++++++++++++++
默认是自上向下执行bean,depends-on可以改变默认顺序
<bean id=“manager” class=“cn.lovepi.***.CacheManager”depends-on=“sysInit”/> <bean id=“sysInit” class=“cn.lovepi.***.SysInit” /> |
++++++++++++++++++++++++++++++++
静态工厂:工厂本身不用创建对象,通过静态方法调用,对象 = 工厂类.工厂方法名();
实例工厂:工厂本身需要创建对象;对象 = 工厂实例.工厂方法名();
Plane.java
package com.bean; public class Plane { private String name; private String size; private String color; @Override public String toString() { // TODO Auto-generated method stub return super.toString(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSize() { return size; } public void setSize(String size) { this.size = size; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } } |
①使用静态工厂创建的bean
package com.factory; import com.bean.Plane; public class StaticFactory { public static Plane makePlane(String method) { Plane plane = new Plane(); plane.setName(method + "工厂造飞机"); plane.setColor("黑色白色"); plane.setSize("120米"); return plane; } } |
<bean class="com.factory.StaticFactory" id="staticFactory" factory-method="makePlane"> <constructor-arg value="静态工厂"></constructor-arg> </bean> |
②使用实例工厂创建的bean
package com.factory; import com.bean.Plane; public class ObjectFactory { public Plane makePlane(String method) { Plane plane = new Plane(); plane.setName(method + "工厂造飞机"); plane.setColor("白色"); plane.setSize("110米"); return plane; } } |
<bean class="com.factory.ObjectFactory" id="objectFactoryBean" > </bean> <!-- 1.先配置实例工厂对象 2.配置我们要创建的对象使用哪个对象创建 ① factory-bean 指定使用哪个工厂实例 ② factory-method 指定使用哪个工厂方法 --> <bean class="com.bean.Plane" id="objectFactory2" factory-bean="objectFactoryBean" factory-method="makePlane"> <constructor-arg value="实例工厂"></constructor-arg> </bean> |
③实现FactoryBean的工厂
package com.factory; import org.springframework.beans.factory.FactoryBean; import com.bean.Book; /** * 实现FactoryBean的类,Spring会识别为工厂类 * Spring会自动调用工厂方法创建实例 */ public class FactoryBeanImplements implements FactoryBean<Book>{ @Override public Book getObject() throws Exception { Book book = new Book(); book.setAuthor("机械工业出版社"); book.setBookName("java web从入门到放弃"); return book; } @Override public Class< ?> getObjectType() { return Book.class; } @Override public boolean isSingleton() { return false; } } |
<!-- FactoryBean是Spring规定的一个接口 只要是实现这个接口的类,Spring都认为是一个工厂 --> <bean class="com.factory.FactoryBeanImplements" id="FactoryBean"> </bean> |
Bean的生命周期
Book.java
public void initMethod() { System.out.println("init初始化"); } public void destroy() { System.out.println("销毁"); } |
单例(scope=”singleton”) 容器启动构造器-》初始化方法=》(容器关闭)销毁方法
<bean class="com.bean.Book" id="book01" init-method="initMethod" destroy-method="destroy" scope="singleton"></bean> |
@Test public void test() { ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml"); ioc.close(); } |
输出
init初始化
…
销毁
多实例(scope=”prototype”) 获取Bean构造器=》初始化方法=》(容器关闭)不自动销毁Bean,需要手动销毁
<bean class="com.bean.Book" id="book011" init-method="initMethod" destroy-method="destroy" scope="prototype"></bean> |
@Test public void test() { ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml"); Book book = (Book)ioc.getBean("book011"); ioc.close(); // (容器关闭)不自动销毁Bean //book.destroy(); 需要手动销毁 } |
Bean后置处理器
package com.bean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class BeanPostProcessorDemo implements BeanPostProcessor{ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("bean初始化之前的方法"+beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("bean初始化之后的方法"+beanName); return bean; } } |
<?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"> <bean class="com.bean.Book" id="book011" init-method="initMethod" destroy-method="destroy" scope="prototype"></bean> <bean class="com.bean.BeanPostProcessorDemo"></bean> </beans> |
@Test public void test() { ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml"); Book book = (Book)ioc.getBean("book011"); ioc.close(); } |
运行后输出如下:
bean初始化之前的方法book011
init初始化
bean初始化之后的方法book011
Spring管理连接池
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/tubu_dangqian"></property> <property name="user" value="root"></property> <property name="password" value="root"></property> <property name="driverClass" value=" "></property> </bean> |
@Test public void jdbc() throws SQLException { // 1.从容器中拿到连接池 //DataSource dataSource = (DataSource) ioc.getBean("dataSource"); // 2.按照类型获取组件,可以获取到这个类型下的所有实现子类等 DataSource dataSource = ioc.getBean(DataSource.class); System.out.println(dataSource.getConnection()); } |
spring连接池引用外部配置文件
dbconfig.properties
username=root password=root jdbcUrl=jdbc:mysql://localhost:3306/tubu_dangqian driverClass=com.mysql.jdbc.Driver |
ioc.xml
<!-- 加载外部配置文件 固定写法classpath表示引用类路径下的一个资源 --> <context:property-placeholder location="classpath:dbconfig.properties"/> <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"> <property name="jdbcUrl" value="${jdbcUrl}"></property> <property name="user" value="${username}"></property> <property name="password" value="${password}"></property> <property name="driverClass" value="${driverClass}"></property> </bean> |
基于xml的自动装配
①autowire=”default” / autowire=”no”:不自动装配,不自动为car属性赋值赋值
②autowire=”byName”: 按照名字赋值
private Car car;以属性名car作为ID去容器中找到这个组件,给他赋值,如果找不到就装配null
③autowire=”byType”:以属性的类型
作为查找依据去容器中找到这个组件;如果容器中有多个这种类型的组件,就会报错
car = ioc.getBean(Car.class)
④autowire=”constructor”:按照构造器进行赋值
- 先按照
有参构造器参数的类型
进行装配,成功就赋值;没有就报错(但是视频上是装配null) - 如果按照类型找到了多个,参数的名字作为id继续装配;找到就装配,找不到就装配null
Person.java中的构造方法
public Person(Car car) { System.out.println("构造方法"); this.car = car; } |
<bean id="car" class="com.bean.Car"> <property name="carName" value="宝马汽车"></property> </bean> <bean id ="person07" class="com.bean.Person" autowire="constructor"> </bean> |
SpEL(Spring Expression Language),spring表达式语言
<bean id="person08" class="com.bean.Person"> <!-- 引用其他bean的某个属性 --> <property name="lastName" value="#{car.carName}"></property> <!-- 字面量 --> <property name="age" value="#{12*5}"></property> <!-- 引用其他bean --> <property name="car" value="#{car}"></property> <!-- 调用静态方法 #{T(全类名).静态方法 --> <property name="email" value="#{T(java.util.UUID).randomUUID().toString()}@qq.com"></property> <!-- 调用静态方法 对象.非静态方法 #{对象.非静态方法} --> <property name="gender" value="#{car.getCarName()}"></property> </bean> |
通过注解分别创建Dao,Service,Servlet
导入jar包spring-aop-4.1.2.RELEASE.jar
某个类上添加上任何一个注解都能快速的将bean加入到这个ioc容器的管理中
spring注解
- @Controller 控制器:我们推荐给控制器层servlet包下的这些组件加这个注解 import org.springframework.stereotype.Controller;
- @Service 业务逻辑:我们推荐业务逻辑层的组件添加这个注解 import org.springframework.stereotype.Service;
- @Repository 给数据库层(持久化层,dao层)的组件添加这个注解 import org.springframework.stereotype.Repository;
- @Component 给不属于以上基层的组件添加这个注解
使用注解将组件快速的加入到容器中需要几步:
- 给要添加的组件上标四个注解的任何一个
- 告诉Spring自动扫描注解的加了注解的组件,以来context名称空间
application.xml
<!-- context:component-scan 自动组件扫描 base-package 指定扫描的基础包,把基础包及他下面所有包的所有加了注解的类,自动扫描到ioc容器中 --> <context:component-scan base-package="com.wuxinzhe.service"></context:component-scan> |
Servicec.java
package com.wuxinzhe.service; import org.springframework.stereotype.Service; @Service public class ServiceC { } |
Test.java
ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml"); @org.junit.Test public void test() { ServiceC bean = (ServiceC)ioc.getBean("serviceC"); // 为类名首字母小写,组件的作用域,默认就是单实例 System.out.println(bean); } |
基于注解加入到ioc容器中组件的默认行为调整
Dao.java
package com.wuxinzhe.dao; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; @Repository("daoChangeName") @Scope(value = "prototype") public class Dao { } |
application.xml
<context:component-scan base-package="com.wuxinzhe.dao"></context:component-scan> |
Test.java
package com.wuxinzhe.test; import com.wuxinzhe.dao.Dao; import static org.junit.Assert.*; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml"); @org.junit.Test public void test() { Object bean = ioc.getBean("daoChangeName"); Object bean2 = ioc.getBean("daoChangeName"); System.out.println(bean); System.out.println(bean2); } } |
输出如下:
com.wuxinzhe.dao.Dao@2d710f1a
com.wuxinzhe.dao.Dao@29215f06
context:exclude-filter指定扫描包时,不包含的类
- type=”annotation” (常用) 指定注解进行排除,标注了指定注解的不要 expression:注解的全类名,例如 org.springframework.stereotype.Service org.springframework.stereotype.Controller…
- type=”assignable” (常用)指定类进行排除,标注了指定类不要 expression:标注的全类名,例如com.wuxinzhe.service.ServiceC
- type=”aspectj”
- type=”custom”
- type=”regex”
application.xml
<context:component-scan base-package="com.wuxinzhe"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:exclude-filter type="assignable" expression="com.wuxinzhe.servlet.Servlet"/> </context:component-scan> |
context:include-filter指定扫描要包含的类
指定扫描包时,要包含的类,只扫描进入哪些组件,默认是全部扫描进来。一定要禁用掉默认规则才有用 use-default-filters=”false” :这个属性,默认为true,会扫描到@Service与@Reposity等,所以配置成false,只扫描白名单中的bean注解。
application.xml
<context:component-scan base-package="com.wuxinzhe" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> |
使用@Autowired注解实现根据类型实现自动装配
application.xml
<context:component-scan base-package="com.wuxinzhe"> </context:component-scan> |
Servlet.java
@Controller public class Servlet { @Autowired private ServiceC service; public void doGet() { System.out.println("servlet.doGet..."); service.save(); } } |
Service.java
@Service public class ServiceC { @Autowired private Dao dao; public void save() { System.out.print("serviceC.save方法。。。"); dao.saveData(); } } |
Dao.java
@Repository("daoChangeName") @Scope(value = "prototype") public class Dao { public void saveData() { System.out.println("dao正在保存数据"); } } |
如果资源类型的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean,进行装配
@Autowired
先按照类型去容器中找到对应的组件
1.找到一个,找到就赋值
2.没找到,抛出异常
3.找到多个?装配上?
①按照变量名作为id继续匹配:private ServiceB serviceB; private ServiceB serviceC;
ServiceB.java
package com.wuxinzhe.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.wuxinzhe.dao.Dao; @Service public class ServiceB { @Autowired private Dao dao; public void save() { System.out.print("serviceB.save方法。。。"); dao.saveData(); } } |
ServiceC.java
package com.wuxinzhe.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.wuxinzhe.dao.Dao; @Service public class ServiceC extends ServiceB{ @Autowired private Dao dao; public void save() { System.out.print("serviceC.save方法。。。"); dao.saveData(); } } |
Servlet.java
package com.wuxinzhe.servlet; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import com.wuxinzhe.service.ServiceB; @Controller public class Servlet { @Autowired private ServiceB serviceC; public void doGet() { System.out.println("servlet.doGet..."); serviceC.save(); } } |
②没有匹配上,则报错,原因,是因为按照变量名进行匹配的,可以采用以下方式避免错误@Qualifier(“serviceC”) 指定一个id,让spring装配
@Controller public class Servlet { @Qualifier("serviceC") @Autowired private ServiceB serviceD; public void doGet() { System.out.println("servlet.doGet..."); serviceD.save(); } } |
③没有匹配上,也可以通过设置@Autowired(required=false)避免异常
@Controller public class Servlet { @Qualifier("serviceCCC") @Autowired(required=false) private ServiceB serviceD; public void doGet() { System.out.println("servlet.doGet..." + serviceD); // serviceD.save(); } } |
输出如下
servlet.doGet…null
在方法的形参位置使用@Qualifier注解
/**方法上有@Autowired的话 * 这个方法会在bean创建的时候自动运行 * 这个方法上的每一个参数都会自动注入值 */ @Autowired public void doGet2(@Qualifier("serviceC")ServiceB serviceB,Dao dao) { System.out.println("doGet2"+serviceB+dao); } |
Spring单元测试
package com.wuxinzhe.test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.wuxinzhe.dao.Dao; import com.wuxinzhe.service.ServiceB; import org.junit.Test; /** * 导入 spring-test-4.1.2.RELEASE.jar * @ContextConfiguration(locations="")使用它来指定spring配置文件的位置 * @RunWith指定用哪种驱动进行测试,如果不指定则默认就是Junit * 以前的注解是由Junit执行 */ @ContextConfiguration(locations="classpath:application.xml") @RunWith(SpringJUnit4ClassRunner.class) public class IOCTest { // ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml"); ApplicationContext ioc = null; @Autowired private ServiceB serviceB; @Autowired private Dao dao; @Test public void test() { System.out.println(serviceB + "" + dao); } } |
泛型依赖注入效果
文件最终路径
application.xml
<context:component-scan base-package="com.wuxinzhe"></context:component-scan> |
User.java
package com.wuxinzhe.bean; public class User { } |
Book.java
package com.wuxinzhe.bean; public class Book { } |
BaseDao.java
package com.wuxinzhe.dao; public abstract class BaseDao<T> { public abstract void save(); } |
BookDao.java
package com.wuxinzhe.dao; import org.springframework.stereotype.Repository; import com.wuxinzhe.bean.Book; @Repository public class BookDao extends BaseDao<Book>{ @Override public void save() { // TODO Auto-generated method stub System.out.println("图书用户Dao正在保存。。。"); } } |
UserDao.java
package com.wuxinzhe.dao; import org.springframework.stereotype.Repository; import com.wuxinzhe.bean.User; @Repository public class UserDao extends BaseDao<User> { @Override public void save() { // TODO Auto-generated method stub System.out.println("用户Dao正在保存。。。"); } } |
BaseService.java
package com.wuxinzhe.service; import org.springframework.beans.factory.annotation.Autowired; import com.wuxinzhe.dao.BaseDao; public class BaseService<T> { @Autowired private BaseDao<T> baseDao; public void save() { System.out.println("自动注入的Dao:" + baseDao); baseDao.save(); } } |
UserService.java
package com.wuxinzhe.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.wuxinzhe.bean.User; import com.wuxinzhe.dao.UserDao; @Service public class UserService extends BaseService<User>{ } |
BookService.java
package com.wuxinzhe.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.wuxinzhe.bean.Book; import com.wuxinzhe.dao.BookDao; @Service public class BookService extends BaseService<Book>{ } |
IocTest.java
package com.wuxinzhe.test; import static org.junit.Assert.*; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.wuxinzhe.service.BookService; import com.wuxinzhe.service.UserService; public class IocTest { ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml"); @Test public void test() { UserService userService = ioc.getBean(UserService.class); BookService bookService = ioc.getBean(BookService.class); bookService.save(); userService.save(); } } |
输出如下:
自动注入的Daocom.wuxinzhe.dao.BookDao@f09733f
图书用户Dao正在保存。。。
自动注入的Daocom.wuxinzhe.dao.UserDao@e350b40
用户Dao正在保存。。。
泛型依赖注入原理
输出带泛型的父类类型
System.out.println(bookService.getClass().getGenericSuperclass()); |
输出如下:
com.wuxinzhe.service.BaseService<com.wuxinzhe.bean.Book> |
AOP
AOP(Aspect Oriented Programming)面向切面编程,是基于OOP基础之上新的编程思想,在程序运行期间,将某段代码动态的切入到指定方法的指定位置进行运行的编程方式。
动态代理
Calculator.java
package com.wuxinzhe.inter; public interface Calculator { public int add(int a,int b); public int minus(int a,int b); public int multi(int a,int b); public int div(int a,int b); } |
MyCalculator.java
package com.wuxinzhe.impl; import com.wuxinzhe.inter.Calculator; public class MyCalculator implements Calculator{ @Override public int add(int a,int b) { int result = a + b; return result; } @Override public int minus(int a, int b) { int result = a - b; return result; } @Override public int multi(int a, int b) { int result = a * b; return result; } @Override public int div(int a, int b) { int result = a / b; return result; } } |
CalculatorProxy.java
package com.wuxinzhe.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; import com.wuxinzhe.impl.MyCalculator; import com.wuxinzhe.inter.Calculator; import com.wuxinzhe.log.LogUtils; /** * 帮Calculator.java生成代理对象的类 Object newProxyInstance */ public class CalculatorProxy { /** * @param calculator 被代理对象 * @return 代理对象 */ public static Calculator getProxy(MyCalculator calculator) { // 被代理对象的类加载器 ClassLoader loader = calculator.getClass().getClassLoader(); // 被代理对象实现的接口 Class<?>[] interfaces = calculator.getClass().getInterfaces(); // 方法执行器,帮我们目标对象执行目标方法 InvocationHandler h = new InvocationHandler() { /** * Object proxy 代理对象,给jdk使用,任何时候都不要动这个对象 Method method 当前将要执行目标对象的方法 Object[] * args 这个方法调用时,外界传入的值 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 利用反射执行目标方法 // 目标方法执行后的返回值 Object result = null; try { LogUtils.start(method,args); result = method.invoke(calculator, args); } catch (Exception e) { LogUtils.exceptionMsg(e); } finally { } // 返回值必须返回出去,外界才能拿到真正执行后的返回值 return result; } }; // Proxy为目标对象创建代理对象 Object proxy = Proxy.newProxyInstance(loader, interfaces, h); // 返回代理对象 return (Calculator) proxy; } } |
LogUtils.java
package com.wuxinzhe.log; import java.lang.reflect.Method; import java.util.Arrays; public class LogUtils { public static void start(Method method, Object[] args) { System.out.println(method.getName() + "方法开始执行"); System.out.println("参数" + Arrays.asList(args)); } public static void exceptionMsg(Exception e) { System.out.println(e.getCause()); } } |
AopTest.java
package com.wuxinzhe.test; import static org.junit.Assert.*; import org.junit.Test; import com.wuxinzhe.impl.MyCalculator; import com.wuxinzhe.inter.Calculator; import com.wuxinzhe.proxy.CalculatorProxy; public class AopTest { @Test public void test() { MyCalculator calculator = new MyCalculator(); Calculator proxy = CalculatorProxy.getProxy(calculator); int result2 = proxy.add(3, 4); System.out.println(result2); } } |
1.写起来难
2.jdk默认的动态代理,如果目标对象没有实现任何接口,是无法为他创建代理对象的,Spring实现了AOP功能,底层就是动态代理。
1.可以利用Spring一句代码都不写的去创建动态代理,实现简单,而且没有强制要求目标对象必须实现借口,将某段代码(比如日志)动态的切入(不把日志代码写死在业务逻辑方法中)到指定方法(加减乘除)的指定位置(方法的开始、结束、异常)进行运行的这种编码方式(Spring简化了面向切面编程)
AOP术语
AOP简单配置
spring-aspects-4.1.2.RELEASE.jar基础版
加强版的面向切面编程(即使目标对象没有实现任何接口也能创建动态代理)
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
AOP简单配置四步骤
1.将目标类和切面类封装了通知方法(在目标方法执行前后执行的方法)加入到ioc容器中
2.还应告诉Spring到低哪个是切面类@Aspect
3.告诉Spring,切面类里面的每一个方法,都是何时何地运行
4.开启基于注解的AOP功能:aop名称空间
Caluculator.java同上
MyCalculator.java
package com.wuxinzhe.impl; import org.springframework.stereotype.Service; import com.wuxinzhe.inter.Calculator; @Service public class MyCalculator implements Calculator{ @Override public int add(int a,int b) { int result = a + b; return result; } @Override public int minus(int a, int b) { int result = a - b; return result; } @Override public int multi(int a, int b) { int result = a * b; return result; } @Override public int div(int a, int b) { int result = a / b; return result; } } |
LogUtils.java
package com.wuxinzhe.log; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class LogUtils { /** * 告诉Spring每个方法都什么时候运行 * 通知注解 * @Before:告诉在目标方法之前运行 前置通知 * @After:告诉在目标方法运行结束之后运行 后置通知 * @AfterReturning:在目标方法正常返回之后 返回通知 * @AfterThrowing:在目标方法抛出异常之后运行 异常通知 * @Around:环绕 环绕通知 */ // 目标方法执行之前执行 @Before("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))") public static void logStart() { System.out.println("开始执行"); } // 目标方法正常执行完成之后执行 @AfterReturning("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))") public static void logEnd() { System.out.println("方法有返回值"); } // 目标方法出现异常的时候执行 @AfterThrowing("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))") public static void exceptionMsg() { System.out.println("出现异常的时候"); } // 目标方法结束的时候执行 @After("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))") public static void logFinally() { System.out.println("最终执行方法"); } } |
application.xml
<context:component-scan base-package="com.wuxinzhe"></context:component-scan> <!-- 开启基于注解的AOP功能:aop名称空间 --> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> |
AopTest.java
package com.wuxinzhe.test; import static org.junit.Assert.*; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.wuxinzhe.inter.Calculator; public class AopTest { ApplicationContext ioc = new ClassPathXmlApplicationContext("application.xml"); @Test public void test() { // 1.从ioc容器中拿到目标对象,注意,如果要用类型,一定用他的接口类型,不要用它本类 Calculator calculator = ioc.getBean(Calculator.class); calculator.div(1,2); } } |
AOP的底层就是动态代理,容器中保存的组件是他的代理对象
cglib为没有接口的组件也可以创建代理对象
切入点表达式方法(通配符)
1.匹配一个或者多个字符
execution(public int com.wuxinzhe.impl.My*.*(int , int ))
2.匹配任意一种类型的参数:
execution(public int com.wuxinzhe.impl.My*.*(int , * ))
3.匹配任意多个参数,任意类型参数
execution(public int com.wuxinzhe.impl.My*.*(..))
4…任意多层路径
execution(public int com..impl.My*.*(..))
5.任意包任意类任意方法,任意参数,不建议使用
execution(* * (..))
通知方法的执行顺序
JoinPoint获取目标方法的信息
import org.aspectj.lang.JoinPoint; // 目标方法执行之前执行 @Before("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))") public static void logStart(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); Signature signature = joinPoint.getSignature(); String name = signature.getName(); System.out.println("参数"+Arrays.asList(args)); System.out.println("signature:"+signature); System.out.println("signature.getName():"+name); System.out.println("开始执行"); } |
输出如下:
参数[1, 2]
signature:int com.wuxinzhe.impl.MyCalculator.add(int,int)
signature.getName():add
returning告诉Spring用result这个变量来接收返回值
// 目标方法正常执行完成之后执行 @AfterReturning(value="execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))",returning="result") public static void logEnd(JoinPoint joinpoint,Object result) { System.out.println("返回值"+result); } |
throwing告诉Spring用exception用这个变量来接收异常值
// 目标方法出现异常的时候执行 @AfterThrowing(value="execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))",throwing="exception") public static void exceptionMsg(JoinPoint joinpoint,Exception exception) { Signature signature = joinpoint.getSignature(); String name = signature.getName(); System.out.println(name+"方法出现异常"); System.out.println(exception.getMessage()); } |
抽取可重用的切入点表达式
①定义
/**抽取可重用的切入点表达式 * 1.随便声明一个没有实现的返回void的方法 * 2.给方法上标注一个@Pointcut注解 */ @Pointcut("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))") public void getFun() {}; |
②使用
@Before(value = "getFun()") public static void logStart(JoinPoint joinPoint) { System.out.println("开始执行"); } |
@Around环绕通知
切面类 LogUtils
package com.wuxinzhe.log; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect @Component public class LogUtils2 { @Pointcut("execution(public int com.wuxinzhe.impl.MyCalculator.*(int , int ))") public void getFun() {}; /** * @throws Throwable * @Around * 环绕通知是Spring中强大的通知 * 动态代理 * try{ * //前置通知 * method.invoke(); * //返回通知 * }catch(e){ * //异常通知 * }finally{ * //后置通知 * } * 四合一通知就是环绕通知 */ @Around(value = "getFun()") public Object myAround(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); // 利用反射调用目标方法 ,等同于method.invoke(obj.arg); Object proceed = null; try { System.out.println("方法执行之前"); proceed = pjp.proceed(args); System.out.println("方法执行结束"); } catch (Exception e) { System.out.println("触发异常"); }finally { System.out.println("最终执行"); } System.out.println("调用反射后的返回值"+proceed); // 反射调用后的返回值 return proceed; } } |
AopTest.java
@Test public void test() { // 1.从ioc容器中拿到目标对象,注意,如果要用类型,一定用他的接口类型,不要用它本类 Calculator calculator = ioc.getBean(Calculator.class); calculator.add(1,2); //calculator.div(2,0); } |
输出结果:
方法执行之前
方法执行结束
最终执行
调用反射后的返回值3
环绕通知执行顺序
环绕通知执行之前
普通通知开始执行
环绕通知方法执行结束
环绕通知最终执行
环绕通知返回值3
普通通知最终执行方法
普通通知返回值3
环绕通知执行之前
普通通知开始执行
环绕通知触发异常
环绕通知最终执行
环绕通知返回值null
普通通知最终执行方法
普通通知返回值null
多切面运行顺序
LogUtils中加入环绕通知
AOP使用场景
基于配置的AOP
<?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:aop="http://www.springframework.org/schema/aop" 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/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> <bean class="com.wuxinzhe.log.LogUtils3" id="log"></bean> <bean class="com.wuxinzhe.impl.MyCalculator" id="myCalculator"></bean> <aop:config> <aop:pointcut expression="execution(* com.wuxinzhe.impl.*.*(..))" id="globalPoint"/> <aop:aspect ref="log"> <aop:pointcut expression="execution(* com.wuxinzhe.impl.*.*(..))" id="onePoint"/> <aop:before method="logStart" pointcut-ref="onePoint"/> <aop:after-returning method="logEnd" pointcut="execution(* com.wuxinzhe.impl.*.*(..))" returning="result"/> <aop:after method="logFinally" pointcut="execution(* com.wuxinzhe.impl.*.*(..))"/> <aop:after-throwing method="exceptionMsg" pointcut="execution(* com.wuxinzhe.impl.*.*(..))" throwing="exception"/> <aop:around method="myAround" pointcut-ref="globalPoint"/> </aop:aspect> </aop:config> </beans> |
注解:快速方便;配置:功能完善;重要的用配置,不重要的用注解。
jdbcTemplate
创建测试数据时候报错
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘dataSource’ defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector
解决方案:
加入jar包mchange-commons-java-0.2.12.jar
1.导入springjdbc数据库模块
spring-jdbc-4.0.0.RELEASE
spring-orm-4.0.0.RELEASE
spring-tx-4.0.0.RELEASE
2.配置ioc文件
<context:property-placeholder location="classpath:dbconfig.properties" /> <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> |
3.执行sql操作
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class); |
①更新
String sql = "UPDATE test1 SET name = ? WHERE id = ?"; int a = jdbcTemplate.update(sql, "小明",18); |
②批量插入
String sql = "insert into test1(name,age,sex,content) VALUES(?,?,?,?)"; List<Object[]> object = new ArrayList<Object[]>(); object.add(new Object[] { "小明", 11, "男", "帅帅哒" }); object.add(new Object[] { "小莉莉", 22, "女", "萌萌哒" }); int[] result = jdbcTemplate.batchUpdate(sql, object); for (int i : result) { System.out.println(i); } |
③查询单个对象
/**查询时,javaBean需要和数据库中字段名保持一致,否则无法完成封装 * 查询集合jdbcTemplate.query() * 查询单个对象jdbcTemplate.queryForObject(),如果查询结果为空,则会抛出报错,则用try..catch捕获异常避免错误发生 */ @Test public void test4() { String sql = "SELECT id,name,age,sex,content FROM test1 WHERE id = ?"; User user = null; try { // RowMapper 每一行记录和JavaBean的属性如何映射 user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 29000); } catch (DataAccessException e) { // TODO Auto-generated catch block } System.out.println(user); System.out.println(user.getId()); } |
④查询集合
String sql = "SELECT id,name,age,sex,content FROM test1 WHERE id > ?"; List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class), 1); // System.out.println(users); for (User user : users) { System.out.println(user.getName()); } |
⑤查询最大值
String sql = "SELECT MAX(id) FROM test1"; //System.out.println(jdbcTemplate.queryForInt(sql)); System.out.println(jdbcTemplate.queryForObject(sql, Integer.class)); |
⑥使用具名参数插入记录
配置ioc容器文件
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> |
String sql = "insert into test1(name,age,sex,content) VALUES(:name,:age,:sex,:content)"; NamedParameterJdbcTemplate namedParameterJdbcTemplate = ioc.getBean(NamedParameterJdbcTemplate.class); Map<String,Object> paramMap = new HashMap<>(); paramMap.put("name","佟丽娅"); paramMap.put("age",33); paramMap.put("sex","女"); paramMap.put("content","我的女神哦~"); int res = namedParameterJdbcTemplate.update(sql, paramMap); System.out.println(res); |
⑧以SqlParameterSource形式传入参数值
NamedParameterJdbcTemplate namedParameterJdbcTemplate = ioc.getBean(NamedParameterJdbcTemplate.class); String sql = "insert into test1(name,age,sex,content) VALUES(:name,:age,:sex,:content)"; User user = new User(); user.setName("佟丽娅"); user.setAge(33); user.setSex("女"); user.setContent("我的女神哦~~"); int res = namedParameterJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(user)); System.out.println(res); |
⑨自动装配JdbcTemplate对象
UserDao.java文件
package com.wuxinzhe.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import com.wuxinzhe.test.User; @Repository public class UserDao { @Autowired JdbcTemplate jdbcTemplate; public void save(User user) { String sql = "insert into test1(name,age,sex,content) VALUES(?,?,?,?)"; jdbcTemplate.update(sql, user.getName(),user.getAge(),user.getSex(),user.getContent()); } } |
ioc容器配置文件
<context:component-scan base-package="com.wuxinzhe.dao"></context:component-scan> |
测试文件
@Test public void test9() { UserDao userDao = ioc.getBean(UserDao.class); User user = new User(); user.setName("张XX"); user.setAge(40); user.setSex("男"); user.setContent("风一样的男人"); userDao.save(user); } |
声明式事务
<!-- 事务控制 --> <!-- 1:配置事务管理器(切面)让其进行事务控制,一定导入切面的几个包 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 2:开启基于注解的事务控制模式,依赖tx名称空间--> <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/> |
<!-- 3:给事务方法加注解 --> package com.wuxinzhe.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.wuxinzhe.dao.BookDao; @Service public class BookService { @Autowired BookDao bookDao; @Transactional public void checkOrder(String userName,String isbn) { int bookPrice = bookDao.getBookPrice(isbn); bookDao.updateUserBalance(userName, bookPrice); bookDao.updateBookStock(isbn); } } |
6 事务的隔离级别
6.1 数据库事务并发问题
假设现在有两个事务:Transaction01和Transaction02并发执行。
①脏读
[1]Transaction01将某条记录的AGE值从20修改为30。
[2]Transaction02读取了Transaction01更新后的值:30。
[3]Transaction01回滚,AGE值恢复到了20。
[4]Transaction02读取到的30就是一个无效的值。
②不可重复读
[1]Transaction01读取了AGE值为20。
[2]Transaction02将AGE值修改为30。
[3]Transaction01再次读取AGE值为30,和第一次读取不一致。
③幻读
[1]Transaction01读取了STUDENT表中的一部分数据。
[2]Transaction02向STUDENT表中插入了新的行。
[3]Transaction01读取了STUDENT表时,多出了一些行。
6.2 隔离级别
数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
①读未提交:READ UNCOMMITTED
允许Transaction01读取Transaction02未提交的修改。
②读已提交:READ COMMITTED
要求Transaction01只能读取Transaction02已提交的修改。
③可重复读:REPEATABLE READ
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
④串行化:SERIALIZABLE
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
⑤各个隔离级别解决并发问题的能力见下表
脏读 不可重复读 幻读
READ UNCOMMITTED 有 有 有
READ COMMITTED 无 有 有
REPEATABLE READ 无 无 有
SERIALIZABLE 无 无 无
⑥各种数据库产品对事务隔离级别的支持程度
Oracle MySQL
READ UNCOMMITTED × √
READ COMMITTED √ √
REPEATABLE READ × √(默认值)
SERIALIZABLE √ √
mysql事务操作常用命令
修改MySQL隔离级别
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
如:SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
查询MySQL的隔离级别
SELECT @@global.tx_isolation; //查询全局隔离级别
SELECT @@session.tx_isolation;//查询当前会话隔离级别
SELECT @@tx_isolation;//同上
事务操作
开启事务 start transaction;
提交事务 commit;
回滚事务 rollback;
读未提交下的脏读
读已提交下的不可重复读
/**事务的几个属性 * * 异常分类: * 运行时异常:发生异常时,默认回滚 * 编译异常:通过try...catch或者throw处理异常 发生异常时,默认不回滚 * * noRollbackFor = class[] 类 指定那些异常不回滚,可以指定默认运行时异常,某些异常可以不用回滚(如ArithmeticException) * noRollbackForClassName = String[] 全类名 * * rollbackFor = class[] 类 指定编译时异常不回滚,但是让某些编译时异常可以回滚(如FileNotFoundException) * rollbackForClassName = String[] * * timeout:int超时时间 * readOnly:boolean事务是否为只读事务,可以进行事务优化,不用管事务那一堆了,但是在有增删改的不可以 */ @Transactional(timeout = 3,readOnly = false,noRollbackFor = {ArithmeticException.class},rollbackFor = {FileNotFoundException.class}) public void checkOrder(String userName,String isbn) { int bookPrice = bookDao.getBookPrice(isbn); bookDao.updateUserBalance(userName, bookPrice); /** try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } **/ bookDao.updateBookStock(isbn); //int res = 10/0; //new FileInputStream("D://HAHAHHAA"); } |
事务传播行为
测试①
myltiService.java
@Transactional public void myltiTx() { bookService.checkOrder("Tom", "ISBN-001"); bookService.updateBookPrice("ISBN-002", 900); } |
bookService.java
@Transactional(propagation = Propagation.REQUIRES_NEW) public void checkOrder(String userName,String isbn) { int bookPrice = bookDao.getBookPrice(isbn); bookDao.updateUserBalance(userName, bookPrice); bookDao.updateBookStock(isbn); } @Transactional(propagation = Propagation.REQUIRED) public void updateBookPrice(String isbn,Integer price) { bookDao.updateBookPrice(isbn, price); int res = 10/0; } |
Test.java
MultiService multiService = ioc.getBean(MultiService.class); multiService.myltiTx(); |
结果checkOrder提交成功,updateBookPrice出现异常回滚。
测试②
@Transactional(propagation = Propagation.REQUIRES_NEW) public void checkOrder(String userName,String isbn) { int bookPrice = bookDao.getBookPrice(isbn); bookDao.updateUserBalance(userName, bookPrice); bookDao.updateBookStock(isbn); int res = 10/0; } @Transactional(propagation = Propagation.REQUIRED) public void updateBookPrice(String isbn,Integer price) { bookDao.updateBookPrice(isbn, price); } |
结果checkOrder发生异常,也会影响之后的事务。
如果是REQUIRED,所有的属性都继承于上级事务的属性
本类事务方法之间的调用,就是一个事务。
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:tx="http://www.springframework.org/schema/tx" 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-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"> <context:property-placeholder location="dbconfig.properties"/> <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> </bean> <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> <context:component-scan base-package="com.wuxinzhe"></context:component-scan> <!-- 1:配置事务管理器(切面)让其进行事务控制,一定导入切面的几个包 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 事务控制 --> <aop:config> <aop:pointcut expression="execution(* com.wuxinzhe.service.*.*(..))" id="txPoint"/> <!-- 事务建议:事务增强 advice-ref指向事务管理器的配置 --> <aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"/> </aop:config> <tx:advice id="myAdvice" transaction-manager="dataSourceTransactionManager"> <tx:attributes> <tx:method name="*"/> </tx:attributes> </tx:advice> </beans> |
SpringMVC
导包
导入核心容器包
commons-logging-1.2.jar
spring-aop-4.1.2.RELEASE.jar
spring-beans-4.1.2.RELEASE.jar
spring-context-4.1.2.RELEASE.jar
spring-core-4.1.2.RELEASE.jar
spring-expression-4.1.2.RELEASE.jar
web模块
spring-web-4.1.2.RELEASE.jar
spring-webmvc-4.1.2.RELEASE.jar
通过dispatcherservlet补全在web.xml创建如下内容
<servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <!--springmvc.xml配置文件--> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Map all requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <!-- /和/*都可以拦截请求 /会拦截所有请求,但是不会拦截*.jsp请求,能保证jsp访问正常 /*的范围更大,还会拦截到*.jsp请求,一旦拦截jsp页面就不能正常访问了 --> <url-pattern>/</url-pattern> </servlet-mapping> |
配置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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 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/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> <context:component-scan base-package="com.wuxinzhe"></context:component-scan> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans> |
FirstController.java文件
package com.wuxinzhe.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class FirstController { @RequestMapping("/hello") public String helloWorld() { System.out.println("请求处理成功"); //return "/WEB-INF/pages/success.jsp"; 同下边配置 /* * <bean * class="org.springframework.web.servlet.view.InternalResourceViewResolver"> * <property name="prefix" value="/WEB-INF/pages/"></property> <property * name="suffix" value=".jsp"></property> </bean> */ return "success"; } } |
一个路径必须对应一个方法
类使用@RequestMapping注解,为所有方法创建一个基准路径
method规定请求方式
@RequestMapping(value=”test1″,method = RequestMethod.GET)
params规定请求参数
①规定必须包含的参数名字
@RequestMapping(value=”test2″,params = {“username”})
对应url ?username=value
②规定不能包含的参数名字
@RequestMapping(value=”test2″,params = {“!username”})
③规定必须包含的参数名字,且规定了参数的值
@RequestMapping(value=”test2″,params = {“username=3″}) 参数值必须等于3
@RequestMapping(value=”test2”,params = {“username!=3″}) 参数值必须不等于3
④规定多个参数
@RequestMapping(value=”test2”,params = {“username!=3″,”age”})
对应url ?username=2&age=3
consumes
只接受内容是哪种请求,规定请求中的Content-Type
produces
告诉浏览器返回的内容类型是什么,给响应头加上Content-Type: text/html;charset=UTF-8
@RequestMapping ant风格模糊匹配
?匹配文件名中的一个字符
*匹配文件名中的任意字符
**匹配多层路径
@PathVariable获取路径上的占位符
@RequestMapping(value = “test2/{userName}”)
public String test3(@PathVariable(“userName”) String userName) {
System.out.println(userName);
return “test1”;
}
REST风格请求
如何发送PUT/DELETE请求
①创建一个post请求的表单
②表单中携带一个_method参数,值为PUT或者DELETE
<form method="post" action="book/1"> <input name="_method" value="delete" /> <input type="submit" value="删除图书" /> </form> <form method="post" action="book/1"> <input name="_method" value="put" /> <input type="submit" value="更新图书" /> </form> |
filter过滤器 把普通请求转化为规定形式的请求
<!-- The front controller of this Spring Web application, responsible for handling all application requests --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Map all requests to the DispatcherServlet for handling --> <servlet-mapping> <!--对应WebContent/springmvc-servlet.xml文件--> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
// 查询
@RequestMapping(value="book/{id}",method = RequestMethod.GET) public String getBook(@PathVariable("id") Integer id) { System.out.println("您获取了id为"+id+"的图书"); return "success"; } @RequestMapping(value="book",method = RequestMethod.POST) public String addBook() { System.out.println("添加图书成功"); return "success"; } @RequestMapping(value="book/{id}",method = RequestMethod.PUT) public String updateBook(@PathVariable("id") Integer id) { System.out.println("更新图书" + id + "成功"); return "success"; } @RequestMapping(value="book/{id}",method = RequestMethod.DELETE) public String deleteBook(@PathVariable("id") Integer id) { System.out.println("删除图书" + id + "成功"); return "success"; } |
成功jsp内容
success.jsp 注意:加上isErrorPage=”true” 8.0以上才能响应PUT/DELETE请求
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isErrorPage="true"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> 恭喜您,请求成功~~ </body> </html> |
@RequestParam获取参数
url:?id=3
①默认方式获取参数值,?id= 或者 未传参 获取的值都为空字符串
@RequestMapping(value="test1") public String test1(Integer id) { System.out.println("获取的参数值为" + id); return "success"; } |
②@RequestParam获取参数值,参数默认是必须带的 ?id= 获取的值为空字符串 未传参则报错
@RequestMapping(value="test1") public String test1(@RequestParam("id") String id) { System.out.println("获取的参数值为" + id); return "success"; } |
②@RequestParam其他属性
设置参数非必传 不传则为null
@RequestMapping(value="test1") public String test1(@RequestParam(value="id",required = false)String id) { System.out.println("获取的参数值为" + id); return "success"; } |
设置参数默认值看,不传参的时候则为null
@RequestMapping(value="test1") public String test1(@RequestParam(value="id",defaultValue = "defaultValue") String id) { System.out.println("获取的参数值为" + id); return "success"; } |
获取请求头信息
@RequestMapping(value="test1") public String test1(@RequestHeader("Accept") String accept) { System.out.println(accept); return "success"; } |
@CookieValue 获取cookie信息
required默认值为true,所以首次访问网站时候,cookie信息如下,Cookie: __jsluid=f0bd1f001ae397d3438d9f7d9c82a8b3; Hm_lvt_d7682ab43891c68a00de46e9ce5b76aa=1559743349,不包含JSESSIONID,所以会报错, 将required 改为 false即可。
由于浏览器会在首次访问网站的时候,响应头里包含设置cookie的信息Set-Cookie: JSESSIONID=66A16E314AC3EA9E4DFCF8F6799C605F; Path=/02Rest; HttpOnly
@RequestMapping(value = "cookie") public String test2(@CookieValue(value = "JSESSIONID", required = false) String jessId) { System.out.println("cookie获取成功,参数值为" + jessId); return "success"; } |
传入POJO,Springmvc自动封装
com.wuxinzhe.dao package com.wuxinzhe.dao; public class User { private String userName; private Integer age; private Address address; // 无参构造器 public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } @Override public String toString() { return "User [userName=" + userName + ", age=" + age + ", address=" + address + "]"; } } |
Address.java
package com.wuxinzhe.dao; public class Address { private String province; private String city; private String area; public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getArea() { return area; } public void setArea(String area) { this.area = area; } @Override public String toString() { return "Address [province=" + province + ", city=" + city + ", area=" + area + "]"; } } |
提交表单
<form method="post" action="user"> 用户名:<input type="text" name="userName"/> 年龄:<input type="text" name="age"/> 省:<input type="text" name="address.province"/> 市:<input type="text" name="address.city"/> 县:<input type="text" name="address.area"/> <input type="submit"/> </form> <pre lang="java" escaped="true"> 处理表单 <pre lang="java" escaped="true"> @RequestMapping(value="user",method=RequestMethod.POST) public String userAdd(User user) { System.out.println("用户详细信息" + user); return "success"; } |
虽然数据可以接收,但是发生了中文乱码
用户详细信息User [userName=??????, age=11, address=Address [province=?±±???, city=??????, area=????±±]] |
提交的数据有乱码
请求乱码
GET:改server.xml
<Connector URIencoding="UTF-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" /> |
POST:
request.setCharacterEncoding(“UTF-8”)
web.xml写filter过滤器来设置
设置了filter之后并没有改变乱码,编译器Preferences=》general=》Workspace=》Text file encoding设置为utf-8
解决方案,将CharacterEncodingFilter放在HiddenHttpMethodFilter之前就可以解决乱码问题
<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>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
数据输出:
Map java.util.Map
@RequestMapping("handle01") public String handle01(Map<String,Object> map) { map.put("userName", "佟丽娅"); return "output"; } |
pageContext:${pageScope.userName} session:${ sessionScope.userName } request:${ requestScope.userName} 可以输出 application:${applicationScope.userName} |
Model org.springframework.ui.Model
@RequestMapping("handle02") public String handle02(Model model) { model.addAttribute("userName", "佟丽娅"); return "output"; } |
pageContext:${pageScope.userName} session:${ sessionScope.userName } request:${ requestScope.userName} 可以输出 application:${applicationScope.userName} |
ModelMap org.springframework.ui.ModelMap
@RequestMapping("handle03") public String handle03(ModelMap modelMap) { modelMap.addAttribute("userName", "佟丽娅"); return "output"; } |
pageContext:${pageScope.userName} session:${ sessionScope.userName } request:${ requestScope.userName} 可以输出 application:${applicationScope.userName} |
ModelAndView
@RequestMapping("handle04") public ModelAndView handle04() { ModelAndView mv = new ModelAndView("output"); mv.addObject("userName", "佟丽娅"); return mv; } |
pageContext:${pageScope.userName} session:${ sessionScope.userName } request:${ requestScope.userName} 可以输出 application:${applicationScope.userName} |
Springmvc提供了一种可以临时给Session中保存数据的方式
使用一个注解 @SessionAttributes(只能标注在类上)
@SessionAttributes(value= {“msg”,”userName”})给BindingAwareModelMap中保存数据,或者在ModelAndView中的数据,同时给session中保存一份;
value指定保存数据时,要给session中存放数据的key
@SessionAttributes(types= {String.class})
给BindingAwareModelMap,或者ModelAndView中保存固定数据类型数据,同时给session中保存一份;
@SessionAttributes @SessionAttributes(value= {"msg","userName"},types= {String.class}) @Controller public class OutController { @RequestMapping("handle01") public String handle01(Map<String, Object> map) { map.put("userName", "佟丽娅"); return "output"; } } |
不推荐使用@SessionAttributes,会引发异常,推荐使用原生API。
forward转发
@RequestMapping("handle05") public String handle05(){ //return "forward:/forward.jsp"; // 跳转到根目录下WebContent的forward.js页面 return "forward:/handle04"; } |
redirect重定向
@RequestMapping("handle06") public String handle06(){ return "redirect:handle05"; } |
view-controller
<!-- 发送一个请求直接来到WEB-INF下的页面 path指定哪个请求 view-name指定映射哪个视图,需要开启mvc注解模式 --> <mvc:view-controller path="/haha" view-name="haha"/> <mvc:annotation-driven></mvc:annotation-driven> |
使用表单标签完成数据
@RequestMapping(value = "userAdd") public String userAdd(Model model) { model.addAttribute("depts", department.getDepartments()); //model.addAttribute("employee", new Employee(1001, "张三", "111@qq.com", 1, new Department(104, "D-DD"))); model.addAttribute("employee", new Employee()); return "userAdd"; } |
userAdd.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <h1>员工添加页面</h1> <form:form action="" modelAttribute="employee"> 姓名:<form:input path="lastName"/><br/> 邮箱:<form:input path="email"/><br/> 性别:男<form:radiobutton path="gender" value="0"/> 女<form:radiobutton path="gender" value="1"/> 部门:<form:select path="department.id" items="${depts}" itemLabel="departmentName" itemValue="id"> </form:select> <input type="submit" value="提交"/> </form:form> <!-- <form action="user" method="post"> 姓名:<input type="text" name="lastName"><br> 邮箱:<input type="text" name="email"/> <br> 性别:男<input type="radio" name="gender" value="0"/> 女<input type="radio" name="gender"><br> 部门: <select name="department.id"> <c:forEach items="${depts }" var="dept" > <option value="${dept.id}">${dept.departmentName}</option> </c:forEach> </select> <input type="submit" value="提交"/> </form> --> </body> </html> |
静态资源无法访问问题
<%pageContext.setAttribute("ctp", request.getContextPath()); %> <script src="${ctp}/scripts/jquery-1.9.1.min.js"></script> springmvc.xml中加入以下配置即可: <mvc:default-servlet-handler/> |
自定义转换器
1.实现Converter接口,写一个自定义的类型转换器
2.Converter是ConverterService的组件:
①你的Converter放入ConverSionService中;
②将WebDataBinder中的ConversionService设置成我们这个加了自定义类型转换器的ConversionService
所以在springmvc.xml中配置
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven> <bean class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.wuxinzhe.component.StringToEmployeeConverter"> </bean> </set> </property> </bean> |
日期格式化
①实体类属性上加上注解,规定页面提交的日期格式
@DateTimeFormat(pattern = “yyyy-MM-dd”)
private Date birth;
②配置文件中将ConversionServiceFactoryBean替换为FormattingConversionServiceFactoryBean,只是功能上多了格式化的功能
<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="conversionService"> <property name="converters"> <set> <bean class="com.wuxinzhe.component.StringToEmployeeConverter"> </bean> </set> </property> </bean> |
改为
<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="conversionService"> <property name="converters"> <set> <bean class="com.wuxinzhe.component.StringToEmployeeConverter"> </bean> </set> </property> </bean> |
数字格式化(步骤同上)
@NumberFormat(pattern = “###,###.##”)
后端数据校验
①可以写程序将我们每一个数据取出进行校验,如果校验失败,则跳到添加页面重新填写
②SpringMVC可以使用JSR303来做数据校验
快速使用JSR303:规范–Hibernate Validator(第三方校验框架)
①导入jar包
hibernate-validator-5.0.0.CR2.jar hibernate-validator-annotation-processor-5.0.0.CR2.jar classmate-0.8.0.jar jboss-logging-3.1.1.GA.jar validation-api-1.1.0.CR1.jar |
注意:如果是tomcat7.0以下,将带el的三个jar包放入tomcat的lib文件夹下
②在javabean中加上校验注解,如下
@Length(min=6,max=8) private String lastName; @Email private String email; |
③在Springmvc封装对象的时候,告诉springmvc这个javabean需要校验
@RequestMapping(value="emps",method=RequestMethod.POST) public String addEmp(@Valid Employee employee) |
④如何知道校验结果
给需要校验的javaBean后面紧跟一个BindingResult,这个BindingResult就是封装前一个bean的校验结果
@RequestMapping(value="emps",method=RequestMethod.POST) public String addEmp(@Valid Employee employee,BindingResult result) { if(result.hasErrors()) { System.out.println("有校验错误"); return "userAdd"; }else { employees.save(employee); System.out.print("添加操作"); return "redirect:/emps"; } } |
5.视图中回显错误
<form:errors path="lastName"></form:errors> <form:errors path="email"></form:errors> |
java.lang.ClassNotFoundException: javax.xml.bind.JAXBException
JAXB API是java EE 的API,因此在java SE 9.0 以上不再包含这个 Jar 包。
java 9 中引入了模块的概念,默认情况下,Java SE中将不再包含java EE 的Jar包
而在 java 6/7 / 8 时关于这个API 都是捆绑在一起的
解决方案:下载
javax.activation-1.2.0.jar
jaxb-api-2.3.0.jar
jaxb-core-2.3.0.jar
jaxb-impl-2.3.0.jar
添加到libs下即可
https://repo1.maven.org/maven2/com/sun/xml/bind/jaxb-core/2.3.0/jaxb-core-2.3.0.jar
https://repo1.maven.org/maven2/com/sun/xml/bind/jaxb-impl/2.3.0/jaxb-impl-2.3.0.jar
https://repo1.maven.org/maven2/javax/xml/bind/jaxb-api/2.3.0/jaxb-api-2.3.0.jar
https://repo1.maven.org/maven2/com/sun/activation/javax.activation/1.2.0/javax.activation-1.2.0.jar
普通表单可以获取校验错误信息
@RequestMapping(value="emps",method=RequestMethod.POST) public String addEmp(@Valid Employee employee,BindingResult result) { if(result.hasErrors()) { List<FieldError> fieldErrors = result.getFieldErrors(); for(FieldError fieldError:fieldErrors) { System.out.println("错误字段"+fieldError.getField()); System.out.print(fieldError.getDefaultMessage()); } System.out.println("有校验错误"); return "userAdd"; }else { employees.save(employee); System.out.print("添加操作"); return "redirect:/emps"; } } |
如何定制自己的国际化错误信息
[Email.employee.email 校验规则.隐含模型中这个对象的key.对象的属性
,Email.email, 校验规则.属性名
Email.java.lang.String, 校验规则.属性类型
Email] 校验规则
1.Email.employee.email如果是隐含模型中的employee对象的email属性字段发生了@Email校验错误,
2.Email.email所有的email属性只要发生了@Email校验错误
3.Email.java.lang.String只要是String类型发生了@Email错误
4.Email只要发生了@Email校验错误
1.创建国际化错误文件
errors_en_US.properties
Email.employee.email=email format incorrect!!! Length.lastName=lastName'length between 6 and 8!!! typeMismatch.birth=birth'format incorrect!!! |
errors_zh_CN.properties
Email.employee.email=\u90AE\u7BB1\u683C\u5F0F\u6709\u8BEF\uFF01\uFF01\uFF01 Length.lastName=\u59D3\u540D\u957F\u5EA6\u6709\u8BEF\uFF01\uFF01\uFF01 typeMismatch.birth=\u751F\u65E5\u683C\u5F0F\u6709\u8BEF\uFF01\uFF01\uFF01 |
2.配置javabean
<bean id="messageResource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="errors"></property> </bean> |
动态灵活传入参数
Length.java.lang.String=lastName’length between 6 and 8 {0} {1} {2}!!!
数据校验message指定错误消息
SpringMVC支持ajax
1.导入json包
jackson-annotations-2.1.5.jar jackson-core-2.1.5.jar jackson-databind-2.1.5.jar |
2.在响应方法中加上@ResponseBody注解
@ResponseBody @RequestMapping(value="allemps") public Collection<Employee> getAllEmp() { Collection<Employee> employees = employeeDao.getAll(); return employees; } |
ResponseBody就是将响应数据放在响应体中,如果是对象,jackson将对象转为json格式
SpirngMVC发送json数据给服务器
ajax发送json数据
<script> var emp = { lastName : "哈哈哈", email : "qqq@qq.com" } emp = JSON.stringify(emp) $.ajax({ url : "${ctp}/requestbody", data : emp, type : "POST", contentType : "application/json", success : function() { alert("发送成功") } }) </script> |
@RequestBody接收json数据页面
@RequestMapping(value="requestbody") public String testRequestBody(@RequestBody Employee employee) { System.out.println(employee); return "hello"; } |
HttpEntity获取请求头
@RequestMapping(value="httpEntity") public String httpEntity(HttpEntity<String> httpEntity) { System.out.println(httpEntity); // return "hello"; } |
System.out.println(httpEntity) 输出如下:
<{"lastName":"哈哈哈","email":"qqq@qq.com"},{host=[localhost:8080], connection=[keep-alive], content-length=[45], pragma=[no-cache], cache-control=[no-cache], accept=[*/*], x-requested-with=[XMLHttpRequest], user-agent=[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36], origin=[http://localhost:8080], sec-fetch-site=[same-origin], sec-fetch-mode=[cors], referer=[http://localhost:8080/curd/test_ajax.jsp], accept-encoding=[gzip, deflate, br], accept-language=[en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7], cookie=[JSESSIONID=2372BBFAF89B035C7624882255336F5D; __jsluid=f0bd1f001ae397d3438d9f7d9c82a8b3; Hm_lvt_d7682ab43891c68a00de46e9ce5b76aa=1559743349], Content-Type=[application/json;charset=UTF-8]}> |
ResponseEntity返回响应数据,定制响应头
@RequestMapping(value="responseHttpEntity") public ResponseEntity<String> responseHttpEntity(){ MultiValueMap<String,String> headers = new HttpHeaders(); String body = "<h1>this is a response</h1>"; headers.add("set-cookie", "11111111111"); return new ResponseEntity(body,headers,HttpStatus.OK); } |
Springmvc文件下载
@RequestMapping(value="download") public ResponseEntity<byte[]> download(HttpServletRequest request) throws Exception{ // 获取下载文件的真实路径 ServletContext context = request.getServletContext(); String realPath = context.getRealPath("/scripts/jquery-1.9.1.min.js"); // 读取文件流 FileInputStream is = new FileInputStream(realPath); byte[] tmp = new byte[is.available()]; is.read(tmp); is.close(); HttpHeaders headers = new HttpHeaders(); headers.set("Content-Disposition", "attachment;filename=jquery-1.9.1.min.js"); return new ResponseEntity<byte[]>(tmp,headers,HttpStatus.OK); } |
SpringMVC文件上传
①导入jar包
commons-fileupload-1.2.1.jar
commons-io-2.0.jar
②配置文件
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="#{1024*1024*20}"></property> <property name="defaultEncoding" value="utf-8"></property> </bean> |
③MultipartFile封装文件信息
@RequestMapping(value="upload") public String upload(@RequestParam(value="username",required=false) String userName,@RequestParam(value="headerimg")MultipartFile file,Model model) { System.out.println(userName); System.out.println(file); System.out.println("文件大小"+file.getSize()); System.out.println("文件名字"+file.getName()); System.out.println("文件原始名字"+file.getOriginalFilename()); System.out.println("文件类型"+file.getContentType()); try { file.transferTo(new File("C:\\Users\\Dell\\Desktop\\demo\\"+file.getOriginalFilename())); model.addAttribute("msg","文件上传成功"); } catch (Exception e) { model.addAttribute("msg", "文件上传失败"); } return "hello"; } |
SpringMVC拦截器HandlerInterceptor
拦截器允许运行目标方法进行一些拦截工作,或者目标方法运行之后进行一些其他处理
在目标方法运行之前调用,返回boolean return true, chain.doFilter()放行;return false,chain.doFilter()不放行
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
在目标方法运行之后调用
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
在请求整个完成之后,来到目标页面之后,chain.doFilter()放行,资源响应之后
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
拦截器HandlerInterceptor是一个接口,我们要使用拦截器,必须实现HandlerInterceptor这个接口
preHandle=》目标方法=》postHandle=》页面=》afterCompletion
注意:
①preHandle不放行,则后边的都不执行
②只要preHandle放行,无论怎样,afterCompletion都会执行
多拦截器执行流程
①哪一块不放行,则此拦截器以后的任何流程都没有,但是它之前已经放行了的拦截器的afterCompletion总会执行
②多个拦截器执行流程
preHandle 顺序执行
postHandle 逆序执行
afterCompletion 逆序执行
localeResolver切换国际化
①实现一个本地化信息的javabean
package com.wuxinzhe.component; import java.util.Locale; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.LocaleResolver; public class MyLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale(HttpServletRequest request) { Locale locale = null; String localeStr = request.getParameter("locale"); if (localeStr != null && !"".equals(localeStr)) { // 通过参数,返回参数对应的区域信息 String[] localInfo = localeStr.split("_"); return new Locale(localInfo[0],localInfo[1]); }else { // 通过请求获取默认的区域信息 return request.getLocale(); } } @Override public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { } } |
②注册javabean(id必须用localeResolver)
<bean id="localeResolver" class="com.wuxinzhe.component.MyLocaleResolver"> </bean> |
③视图中就可以和之前一样使用了。
SessionLocaleResolver切换国际化
①配置SessionLocaleResolver
<!-- 区域信息从session中获取 --> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"> </bean> |
②在session中设置区域信息
@RequestMapping(value="/login") public String login(@RequestParam(value="locale",defaultValue="zh_CN") String localeStr,Locale locale,HttpSession session) { String[] localeInfo = localeStr.split("_"); session.setAttribute(SessionLocaleResolver.class.getName()+".LOCALE", new Locale(localeInfo[0],localeInfo[1])); return "login"; } |
③视图中就可以和之前一样使用了。
SessionLocaleResolver配合LocaleChangeInterceptor切换国际化
①配置javabean
<!-- 区域信息从session中获取 --> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"> </bean> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> </bean> |
②就可以从域名+?locale=en_US,通过参数自动设置国际化
默认就是这几个HandlerExceptionResolver
ExceptionHandlerExceptionResolver:@ExceptionHandler
ResponseStatusExceptionResolver:@ResponseStatus
DefaultHandlerExceptionResolver:判断是否是SpringMVC自带的异常
如果异常解析器都不能处理,就直接抛出去
@ExceptionHandler使用
如果一个类中发生了异常,则会使用@ExceptionHandler注解的方法,进行处理。处理异常方法只能接收一个参数,并且返回ModelAndView
@ExceptionHandler
@ExceptionHandler(Exception.class) public ModelAndView exceptionHandler(Exception exception) { ModelAndView view = new ModelAndView("success"); view.addObject("exception", exception); return view; } |
精确指定某个异常
@ExceptionHandler(value= {ArithmeticException.class})
@ControllerAdvice定义一个处理全局异常的类
@ControllerAdvice public class ExceptionTestController { @RequestMapping(value="caculate") public String caculate() { Integer i = 10/0; return "hello"; } } |
本类异常和全局类异常,本类异常优先,无关异常类型。
@ResponseStatus使用
@ResponseStatus定义一个运行时异常类
package com.wuxinzhe.controller; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(reason = "用户未找到", value = HttpStatus.NOT_ACCEPTABLE) public class UserNotFoundException extends RuntimeException { //private static final long serialVersionUID = 1L; } |
在运行时,抛出异常
@RequestMapping(value="caculate") public String caculate() { if(true) { throw new UserNotFoundException(); } return "success"; } |
Spring和SpringMVC整合
ContextLoaderListener
web.xml配置ContextLoaderListener
<!-- needed for ContextLoaderListener --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring.xml</param-value> </context-param> <!-- Bootstraps the root web application context before servlet initialization --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>springmvc</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> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> |
springmvc配置扫描类
<!-- use-default-filters过滤所有,需要禁用 --> <context:component-scan base-package="com.wuxinzhe" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/> </context:component-scan> |
spring.xml配置扫描类
<context:component-scan base-package="com.wuxinzhe"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/> </context:component-scan> |
spring作为父容器,springmvc作为子容器,子容器无法装配父容器的javabean,如下
@Autowired BookController bookController; public BookService() { System.out.println("bookService"); } |
MyBatis
①导入包
mybatis-3.4.1.jar log4j-1.2.17.jar mysql-connector-java-5.1.48-bin.jar |
②创建javabean文件
package com.wuxinzhe.bean; public class Employee { private Integer id; private String employeeName; private Integer gender; public Employee() { super(); } public Employee(Integer id, String employeeName, Integer gender) { super(); this.id = id; this.employeeName = employeeName; this.gender = gender; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } public Integer getGender() { return gender; } public void setGender(Integer gender) { this.gender = gender; } @Override public String toString() { return "Employee [id=" + id + ", employeeName=" + employeeName + ", gender=" + gender + "]"; } } |
③dao接口文件
package com.wuxinzhe.dao; import com.wuxinzhe.bean.Employee; public interface EmployeeDao { // 获取某个雇员信息 public Employee getEmployeeById(Integer Id); // 更新某个雇员信息 public Integer updateEmp(Employee employee); // 添加某个雇员信息 public Integer insertEmp(Employee employee); // 删除某个雇员信息 public boolean deleteEmp(Integer Id); } |
④mybatis-config.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> <!-- properties引入外部配置文件 resource从类路径下引入 url从磁盘或者网络路径引入 --> <properties resource="dbconfig.properties"></properties> <settings> <!-- javabean属性名驼峰str1Str2 自动对应 数据表字段 str1_str2 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <!-- ①为一个javabean起别名,方便引用 引用名默认就是类名,也可以使用alias属性可以指定一个新的别名 --> <!-- <typeAlias type="com.wuxinzhe.bean.Employee" /> --> <!-- ②批量起名 默认名字就是包名,也可以通过Alias注解在类上重新命名 --> <package name="com.wuxinzhe.bean"/> <!-- 不建议使用别名,因为别名不利于读 --> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <!-- 注册写好的sql映射文件 --> <mappers> <!-- <mapper resource="employee.xml"/> --> <!-- 批量注册映射文件 --> <!-- 批量注册文件名必须对应dao名称 --> <package name="com.wuxinzhe.dao" /> </mappers> </configuration> |
⑤在包名为com.wuxinzhe.dao的包下,创建EmployeeDao.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.wuxinzhe.dao.EmployeeDao"> <select id="getEmployeeById" resultType="Employee"> select * from t_employee where id = #{id} </select> <insert id="insertEmp"> INSERT INTO t_employee(employeeName,gender) VALUES(#{employeeName},#{gender}) </insert> <update id="updateEmp"> UPDATE t_employee SET employeeName=#{employeeName},gender=#{gender} WHERE id=#{id} </update> <delete id="deleteEmp"> DELETE * FROM t_employee WHERE id=#{id} </delete> </mapper> |
⑥测试
package com.wuxinzhe.test; import static org.junit.jupiter.api.Assertions.*; import java.io.IOException; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.wuxinzhe.bean.Employee; import com.wuxinzhe.dao.EmployeeDao; class MyBatisTest { SqlSessionFactory sqlSessionFactory; @BeforeEach public void initSqlSessionFatctory() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test void test() throws IOException { //根据全局配置文件创建一个SqlSessionFactory // SqlSessionFactory是SqlSession工厂,负责创建从中获得 SqlSession 的实例 // SqlSession:sql会话(代表和数据库的一次会话) SqlSession sqlSession = sqlSessionFactory.openSession(); try { //获取dao接口实现 EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class); Employee employee = employeeDao.getEmployeeById(1); System.out.println(employee); } finally { sqlSession.close(); } } @Test void InsertEmp() { SqlSession sqlSession = sqlSessionFactory.openSession(); try { EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class); employeeDao.insertEmp(new Employee(null,"哈哈哈",1)); }finally { sqlSession.close(); } } } |
导入dtd写xml配置有提示
http://mybatis.org/dtd/mybatis-3-config.dtd -//mybatis.org//DTD Config 3.0//EN
http://mybatis.org/dtd/mybatis-3-mapper.dtd -//mybatis.org//DTD Mapper 3.0//EN
windows->preferences->XML->xml Catalog->Add
添加时候key为-//mybatis.org//DTD Config 3.0//EN,key type来选择URI,Location选择file systems配置文件路径
注意:mybatis-3-config.dtd mybatis-3-mapper.dtd从Mybatis jar包中获取。
获取自增主键的值
useGeneratedKeys获取自增的主键,通过keyProperty指定封装对象的属性
<insert id="insertEmp" useGeneratedKeys="true" keyProperty="id"> INSERT INTO t_employee(employeeName,gender) VALUES(#{employeeName},#{gender}) </insert> |
获取非自增主键的值
order=”BEFORE”:在sql运行之前运行
keyProperty=”id”:运行后赋值给
resultType=”integer”:返回值类型为整型
<insert id="insertEmp2"> <selectKey order="BEFORE" keyProperty="id" resultType="integer"> SELECT max(id)+1 FROM t_employee </selectKey> INSERT INTO t_employee(id,employeeName,gender) VALUES(#{id},#{employeeName},#{gender}) </insert> |
参数的各种取值
单个参数
#{名字随意}
多个参数
只要传入了多个参数,mybatis会自动的将这些参数封装在一个map中,封装时使用的key就是参数的索引和参数的第几个表示,Map
接口方法
public Employee getEmployeeByIdAndEmpname(@Param(“id”) Integer id,@Param(“empName”) String empName);
sql映射文件
<select id="getEmployeeByIdAndEmpname" resultType="Employee"> select * from t_employee where id = #{id} AND employeeName=#{empName} </select> |
map
POJO
sql语句中${}和#{}取值的区别
#{}参数预编译的方式,防止sql注入,更安全,
${}直接拼接sql语句,不安全,在sql语句中有些位置是不支持预编译的,如表名,就得用${}方式取值
查询返回List resultType返回list的集合里面的元素类型
public List<Employee> allEmps(); </[re?> <pre lang="conf" escaped="true"> <select id="allEmps" resultType="Employee"> SELECT * FROM t_employee </select> |
返回单条记录封装成map
接口方法
public Map<String,Object> getEmpByIdReturnMap(Integer id); |
sql映射文件
<select id="getEmpByIdReturnMap" resultType="map"> SELECT * FROM t_employee WHERE id=#{id} </select> |
返回多条记录封装成map,@MapKey指定哪个字段作为Map的key,resultType指定Map中的值类型
@MapKey("id") public Map<Integer,Employee> allEmpsReturnMap(); |
<select id="allEmpsReturnMap" resultType="Employee"> SELECT * FROM t_employee </select> |
默认MyBatis自动封装结果集
1.按照列名和属性名一一对应规则(不区分大小写)
2.如果不一一对应
①开启驼峰命名(字段中必须有下划线_)
②sql映射语句字段起别名方式 select id,name employeeName,sex gender FROM t_employee,其中employeeName和gender为封装javabean的属性
③resultMap
resultMap自定义封装规则
resultMap自定义结果集和javabean的映射规则
<select id="getEmployeeById" resultMap="getEmployeeById"> select * from t_employee where id = #{id} </select> <!--type:指定为哪一个javabean封装规则,全类名 id:唯一标识,供sql语句引用--> <resultMap type="Employee" id="getEmployeeById"> <!-- 主键列 id指定主键的对应规则 column指定哪一列是主键列 property指定javabean的哪个属性 --> <id column="id" property="id"/> <!-- 普通列 --> <result column="name" property="employeeName"/> <result column="sex" property="gender"/> </resultMap> |
级联属性
keyDao.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.wuxinzhe.dao.KeyDao"> <select id="getKeyById" resultMap="getKeyById"> SELECT k.id,k.key_name,k.lock_id,l.id l_id,l.lock_name FROM t_key k LEFT JOIN t_lock l ON k.lock_id=l.id WHERE k.id=#{id} </select> <resultMap type="com.wuxinzhe.bean.Key" id="getKeyById"> <id property="id" column="id"/> <result property="keyName" column="key_name" /> <result property="lock.id" column="lock_id"/> <result property="lock.lockName" column="lock_name"/> </resultMap> </mapper> |
推荐采用association达到上例同样的效果
<resultMap type="com.wuxinzhe.bean.Key" id="getKeyById"> <id property="id" column="id" /> <result property="keyName" column="key_name" /> <!--association表示联合了一个对象--> <association property="lock" javaType="com.wuxinzhe.bean.Lock"> <id property="id" column="l_id" /> <result property="lockName" column="lock_name" /> </association> </resultMap> |
collection定义集合类型属性的封装规则
com.wuxinzhe.bean.Lock
private Integer id; private String lockName; private List<Key> keys; |
LockDao.xml
<select id="getLockById" resultMap="getLockById"> SELECT l.id,l.lock_name,k.id kid,k.key_name FROM t_lock l LEFT JOIN t_key k ON k.lock_id=l.id WHERE l.id=#{id} </select> <resultMap id="getLockById" type="com.wuxinzhe.bean.Lock" > <id property="id" column="id"/> <result property="lockName" column="lock_name"/> <collection property="keys" ofType="com.wuxinzhe.bean.Key"> <id property="id" column="kid"/> <result property="keyName" column="key_name"/> <!-- <association property="lock" javaType="com.wuxinzhe.bean.Lock"> <id property="id" column="id" /> </association> --> </collection> </resultMap> |
使用select属性指定分区查询,简化sql语句
LockDao.java
public Lock getLockByIdSimple(Integer id); |
KeyDao.java
public Key getKeyByIdSimple(Integer id); |
KeyDao.xml
<select id="getLockByIdSimple" resultType="com.wuxinzhe.bean.Lock"> SELECT * FROM t_lock WHERE id=#{id} </select> |
LockDao.xml
<select id="getKeyByIdSimple" resultMap="myKey"> SELECT * FROM t_key WHERE id=#{id} </select> <resultMap id="myKey" type="com.wuxinzhe.bean.Key"> <id property="id" column="id"/> <result property="keyName" column="key_name"/> <association property="lock" select="com.wuxinzhe.dao.LockDao.getLockByIdSimple" column="lock_id"> </association> </resultMap> |
测试查询
KeyDao keyDao = sqlSession.getMapper(KeyDao.class); Key key = keyDao.getKeyByIdSimple(1); System.out.println(key); |
输出如下:
Key [id=1, keyName=1号钥匙, lock=Lock [id=1, lockName=1号锁]] |
延迟加载和按需加载
全局配置
<settings> <!-- javabean属性名驼峰str1Str2 自动对应 数据表字段 str1_str2 --> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 开启延迟开关 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 开启属性按需加载 --> <setting name="aggressiveLazyLoading" value="false"/> </settings> |
测试
KeyDao keyDao = sqlSession.getMapper(KeyDao.class); Key key = keyDao.getKeyByIdSimple(1); String keyName = key.getKeyName(); System.out.println(keyName); Thread.sleep(3000); System.out.println(key.getLock()); <pre lang="java" escaped="true"> 输出如下: <pre lang="conf" escaped="true"> DEBUG 02-07 18:48:59,305 ==> Preparing: SELECT * FROM t_key WHERE id=? (BaseJdbcLogger.java:145) DEBUG 02-07 18:48:59,389 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145) DEBUG 02-07 18:48:59,495 <== Total: 1 (BaseJdbcLogger.java:145) 1号钥匙 DEBUG 02-07 18:49:02,499 ==> Preparing: SELECT * FROM t_lock WHERE id=? (BaseJdbcLogger.java:145) DEBUG 02-07 18:49:02,500 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145) DEBUG 02-07 18:49:02,503 <== Total: 1 (BaseJdbcLogger.java:145) Lock [id=1, lockName=1号锁] |
动态sql语句 where
StudentDao.java
public List<Student> getStudentByCondition(Student student); |
<select id="getStudentByCondition" resultType="Student"> SELECT * FROM t_students <where> <if test="address !=null && address!="""> address LIKE #{address} </if> <if test="gender!=null"> AND gender = #{gender} </if> <if test="age!=null"> AND age > #{age} </if> </where> </select> |
@Test void getStudentByCondition() { Student student = new Student(); //student.setAddress("%小区%"); //student.setGender(1); student.setAge(11); SqlSession sqlSession = sqlSessionFactory.openSession(); try { StudentDao studentDao = sqlSession.getMapper(StudentDao.class); System.out.println(studentDao.getStudentByCondition(student)); }finally { sqlSession.close(); } } |
sql语句打印:
DEBUG 02-08 14:16:52,021 ==> Preparing: SELECT * FROM t_students WHERE age > ? (BaseJdbcLogger.java:145) DEBUG 02-08 14:16:52,085 ==> Parameters: 11(Integer) (BaseJdbcLogger.java:145) DEBUG 02-08 14:16:52,120 <== Total: 5 (BaseJdbcLogger.java:145) [Student [id=1, name=小丽, address=哈哈小区, gender=2, age=13], Student [id=2, name=小明, address=牛牛小区, gender=1, age=14], Student [id=3, name=小强, address=奔腾小区, gender=1, age=20], Student [id=4, name=小北, address=吉利小区, gender=1, age=21], Student [id=5, name=小菜, address=奔驰小区, gender=2, age=18]] 可以看出来拼接后的sql语句会把第一个语句的and去掉 |
动态sql语句trim标签
<select id="getStudentByCondition" resultType="Student"> SELECT * FROM t_students <!-- prefix:前缀,如果有条件存在,则为下面整体sql添加一个前缀 prefixOverrides:取出整体字符串开头多余的某个字符串 suffix:为整天添加一个后缀 suffixOverrides:取出整体字符串尾部多余的某个字符串 --> <trim prefix="where" prefixOverrides="and"> <if test="address !=null && address!="""> address LIKE #{address} </if> <if test="gender!=null"> AND gender = #{gender} </if> <if test="age!=null"> AND age > #{age} </if> </trim> </select> |
动态sql语句collection标签
public List<Student> getStudentIn(@Param("ids")List<Integer> ids); |
<select id="getStudentIn" resultType="Student"> SELECT * FROM t_students WHERE id IN <!-- collection默认值为list,如果想自定义参数,可以通过@Param传参 open sql语句以什么开始 close sql语句以什么结束 如果遍历的当前值一个List index 当前索引项的索引 item 指定当前索引项的元素值 如果遍历的当前值时一个Map index 当前索引项的key item 指定当前索引项的元素值 separator每个索引项分隔的符号 --> <foreach collection="ids" open="(" close=")" item="item" separator="," index="index"> #{item} </foreach> </select> |
动态sql语句choose标签(满足任何一个条件,结束语句)
<select id="getStudentByChoose" resultType="Student"> SELECT * FROM t_students <where> <choose> <when test="id !=null"> id=#{id} </when> <when test="gender!=null"> gender=#{gender} </when> <when test="age!=null"> age=#{age} </when> <otherwise> 1=1 </otherwise> </choose> </where> </select> |
使用if结合set动态更新
<update id="updateStudent"> UPDATE t_students <set> <if test="name!=null and name!="""> name=#{name}, </if> <if test="address!=null and address!="""> address=#{address}, </if> <if test="gender!=null"> gender=#{gender}, </if> <if test="age!=null"> age=#{age}, </if> </set> WHERE id=#{id} </update> |
测试文件
Student student = new Student(); student.setId(1); student.setAge(1); student.setAddress("欢城镇"); student.setGender(2); student.setName("哈哈哈"); SqlSession sqlSession = sqlSessionFactory.openSession(); try { StudentDao studentDao = sqlSession.getMapper(StudentDao.class); studentDao.updateStudent(student); }finally { sqlSession.close(); } |
更新执行的sql语句
UPDATE t_students SET name=?, address=?, gender=?, age=? WHERE id=? |
sql和include标签抽取可重用sql语句
一级缓存:线程级别的缓存,本地缓存,SqlSession级别的缓存
二级缓存:全局范围的缓存,除了当前线程,SqlSession能用外,其他都可以用。
一级缓存失效的几种情况以及观察原理
①一级缓存是SqlSession级别的缓存,不同的sqlSession,使用不同的一级缓存,只有在同一个sqlSession期间查询到的数据回报存在SqlSession的缓存中,下次使用这个sqlSession会从缓存中拿
②同一个方法,不同的参数,由于可能之前没查询过,所以还会发新的sql
③在一个SqlSession期间执行任何上一次增删改操作,增删改操作会把缓存清空
④手动清空缓存sqlSession.clearCache();
二级缓存
全局作用域缓存,二级缓存默认不开启,需要手动配置,MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
二级缓存在SqlSession关闭或者提交之后才会生效,每一个Dao有它自己的二级缓存
使用步骤
1.全局配置文件中开启二级缓存
<setting name="cacheEnabled" value="true"/> |
2.需要使用二级缓存的映射文件处使用cache配置缓存
<cache/> |
3.注意:POJO需要实现Serializable接口
缓存的查询顺序
①一级缓存和二级缓存不会有同一个数据
二级缓存中:一级缓存关闭了就有了
一级缓存中:二级缓存中没有此数据,就会看一级缓存,一级缓存没有数据,就去查询数据库,数据库的查询结果,就放在一级缓存中了。
② 任何时候都是先看二级缓存,再看一级缓存,如果大家都没有就去查询数据库
SSM整合
SSM导包
Spring
【IOC核心】
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
commons-logging-1.1.3.jar
【aop】
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
【jdbc】
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
【测试】
spring-test-4.0.0.RELEASE.jar
SpringMVC
【核心】
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar
【上传下载】
commons-fileupload-1.2.1.jar
commons-io-2.0.jar
【jstl-jsp标准标签库】
standard.jar
jstl.jar
【数据校验】
hibernate-validator-5.0.0.CR2.jar
hibernate-validator-annotation-processor-5.0.0.CR2.jar
classmate-0.8.0.jar
jboss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar
【ajax】
jackson-annotations-2.1.5.jar
jackson-core-2.1.5.jar
jackson-databind-2.1.5.jar
MyBatis
【核心包】
mybatis-3.4.1.jar
【ehcache】
ehcache-core-2.6.8.jar
mybatis-ehcache-1.0.3.jar
log4j-1.2.17.jar
slf4j-api-1.7.21.jar
slf4j-log4j12-1.7.21.jar
【数据源/数据库驱动】
mysql-connector-java-5.1.48-bin.jar
c3p0-0.9.5.2.jar
mchange-commons-java-0.2.12.jar
jdk8以上还需要报错java.lang.NoClassDefFoundError:javax/xml/bind/JAXBException
javax.activation-1.2.0.jar
jaxb-api-2.3.0.jar
jaxb-core-2.3.0.jar
jaxb-impl-2.3.0.jar
SSM配置
ehcache.xml
log4j.xml
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>07SSM</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- 配置Spring容器启动 --> <context-param> <param-name>contextConfigLocation</param-name> <!-- 指定Spring配置文件位置 --> <param-value>classpath:spring/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 配置SpringMVC前端控制器 --> <servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <!-- 拦截所有请求 --> <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> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 支持REST风格的过滤器 --> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> |
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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 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-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"> <context:component-scan base-package="com.wuxinzhe"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 导入外部配置文件 --> <context:property-placeholder location="classpath:dbconfig.properties"/> <!-- 配置数据源 --> <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource" > <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property> <property name="minPoolSize" value="${jdbc.minPoolSize}"></property> </bean> <!-- 配置JdbcTemplate操作数据库 --> <!-- 配置Mybatis操作数据库 SqlSessionFactoryBean 可以根据配置文件 --> <bean class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <property name="dataSource" ref="dataSource"></property> <!-- 指定xml映射文件的位置 --> <property name="mapperLocations" value="classpath:mybatis/dao/*.xml"></property> </bean> <!-- 把每一个dao接口的实现加入到ioc容器中 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 指定dao接口所在的包 --> <property name="basePackage" value="com.wuxinzhe.dao"></property> </bean> <!-- 配置事务控制,配置事务管理器,让他控制数据源里边的链接关闭和提交 --> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 基于xml配置,配置事务,哪些方法切入事务还要写切入点表达式 --> <aop:config> <!-- 配置切入点表达式 --> <aop:pointcut expression="execution(* com.wuxinzhe.service.*.*(..))" id="txPoint"/> <aop:advisor advice-ref="myTx" pointcut-ref="txPoint"/> </aop:config> <!-- 配置事务增强、事务属性、事务建议 transaction-manager指定要配置的事务管理id --> <tx:advice id="myTx" transaction-manager="transactionManager"> <!-- 配置事务的属性 --> <tx:attributes> <tx:method name="*" rollback-for="java.lang.Exception"/> <tx:method name="get*" read-only="true"/> <tx:method name="insertEmp" isolation="READ_UNCOMMITTED"/> </tx:attributes> </tx:advice> </beans> |
mybatis-config.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> <!-- properties引入外部配置文件 resource从类路径下引入 url从磁盘或者网络路径引入 --> <properties resource="dbconfig.properties"></properties> <settings> <!-- javabean属性名驼峰str1Str2 自动对应 数据表字段 str1_str2 --> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 开启延迟开关 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 开启属性按需加载 --> <setting name="aggressiveLazyLoading" value="false"/> </settings> <typeAliases> <!-- ①为一个javabean起别名,方便引用 引用名默认就是类名,也可以使用alias属性可以指定一个新的别名 --> <!-- <typeAlias type="com.wuxinzhe.bean.Employee" /> --> <!-- ②批量起名 默认名字就是包名,也可以通过Alias注解在类上重新命名 --> <package name="com.wuxinzhe.bean"/> <!-- 不建议使用别名,因为别名不利于读 --> </typeAliases> <!-- <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> --> <!-- PageHelper分页插件使用 --> <plugins> <!-- com.github.pagehelper为PageHelper类所在包名 --> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 使用下面的方式配置参数,后面会有所有的参数介绍 --> </plugin> </plugins> <!-- 注册写好的sql映射文件 --> <mappers> <!-- <mapper resource="employee.xml"/> --> <!-- 批量注册映射文件 --> <!-- 批量注册文件名必须对应dao名称 --> <package name="mybatis.dao" /> </mappers> </configuration> |
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:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd 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-4.0.xsd"> <!-- springMVC只扫描控制器,需要禁用掉默认规则 --> <context:component-scan base-package="com.wuxinzhe" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"></property> <property name="suffix" value=".jsp"></property> </bean> <!-- 配置文件上传解析器 --> <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver"> <property name="defaultEncoding" value="utf-8"></property> <property name="maxUploadSize" value="#{1024*1024*20}"></property> </bean> <!-- 扫静态资源 --> <mvc:default-servlet-handler/> <!-- 扫动态 --> <mvc:annotation-driven></mvc:annotation-driven> </beans> |
MBG
①导包
mybatis-generator-core-1.3.2.jar
②写配置(本目录下mbg.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> <!-- targetRuntime MyBatis3 生成复杂版的sql MyBatis3Simple 生成简单版CURD sql --> <context id="DB2Tables" targetRuntime="MyBatis3"> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/employee" userId="root" password="root"> </jdbcConnection> <javaTypeResolver > <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- javaModelGenerator 生成POJO targetPackage 生成的POJO放在哪个包里 targetProject 放在哪个工程的哪个目录下 --> <javaModelGenerator targetPackage="com.wuxinzhe.bean" targetProject=".\src"> <property name="enableSubPackages" value="true" /> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- sqlMapGenerator sql映射文件生成器 --> <sqlMapGenerator targetPackage="com.wuxinzhe.dao" targetProject=".\conf"> <property name="enableSubPackages" value="true" /> </sqlMapGenerator> <!-- javaClientGenerator javadao接口生成的地方 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.wuxinzhe.dao" targetProject=".\src"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <!-- table逆向生成哪个数据表 tableName 表名 domainObjectName 这个表对应的对象 --> <!-- <table schema="DB2ADMIN" tableName="ALLTYPES" domainObjectName="Customer" > <property name="useActualColumnNames" value="true"/> <generatedKey column="ID" sqlStatement="DB2" identity="true" /> <columnOverride column="DATE_FIELD" property="startDate" /> <ignoreColumn column="FRED" /> <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" /> </table> --> <table schema="DB2ADMIN" tableName="t_employee" domainObjectName="Employee" > </table> <table schema="DB2ADMIN" tableName="t_lock" domainObjectName="Lock" > </table> <table schema="DB2ADMIN" tableName="t_key" domainObjectName="Key" > </table> </context> </generatorConfiguration> |
③利用mbg生成mybatis相关文件
package com.wuxinzhe.test; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.config.Configuration; import org.mybatis.generator.config.xml.ConfigurationParser; import org.mybatis.generator.exception.InvalidConfigurationException; import org.mybatis.generator.exception.XMLParserException; import org.mybatis.generator.internal.DefaultShellCallback; public class MBGTest { public static void main(String[] args) throws IOException, XMLParserException, Exception { List<String> warnings = new ArrayList<String>(); boolean overwrite = true; File configFile = new File("mbg.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); } } |
批量插入1000条数据
映射文件
<insert id="insertBatch" parameterType="list"> INSERT INTO t_employee(employeeName,gender) VALUES <foreach collection="employees" item="employee" separator=","> (#{employee.employeename},#{employee.gender}) </foreach> </insert> |
批量插入数据dao
void insertBatch(@Param("employees") List<Employee> employees); |
测试文件
@Test void testInsertBatch() { SqlSession sqlSession = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); List<Employee> employees = new ArrayList<Employee>(); for (int i = 0; i <= 1000; i++) { Employee employee = new Employee(); employee.setEmployeename(UUID.randomUUID().toString().substring(0, 5)); employee.setGender(1); employees.add(employee); } mapper.insertBatch(employees); sqlSession.commit(); } finally { sqlSession.close(); } } |
pageHelper分页使用
pageHelper分页插件jar下载地址
https://repo1.maven.org/maven2/com/github/pagehelper/pagehelper/
jsqlparser.jar下载
https://www.mvnjar.com/com.github.jsqlparser/jsqlparser/0.9.5/detail.html
①导包
jsqlparser-0.9.5.jar
pagehelper-5.0.3.jar
②mybatis-config.xml全局配置
<!-- PageHelper分页插件使用 --> <plugins> <!-- com.github.pagehelper为PageHelper类所在包名 --> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 使用下面的方式配置参数,后面会有所有的参数介绍 --> </plugin> </plugins> |
③mapper映射
<select id="getEmpsByPage" resultType="Employee"> SELECT * FROM t_employee </select> |
④dao
public List<Employee> getEmpsByPage(); |
⑤Controller调用
@RequestMapping("getEmpsByPage") public String getEmpsByPage(Model model, @RequestParam(value = "page", defaultValue = "1") Integer page) { PageHelper.startPage(page, 5); //紧跟着的第一个select方法会被分页 List<Employee> lists = employeeService.getEmpsByPage(); PageInfo pageInfo = new PageInfo(lists); //分页列表Page{count=true, pageNum=5, pageSize=5, startRow=20, endRow=25, total=1017, pages=204, reasonable=false, pageSizeZero=false} System.out.println("分页列表" + lists); //分页信息PageInfo{pageNum=5, pageSize=5, size=5, startRow=21, endRow=25, total=1017, pages=204, list=Page{count=true, pageNum=5, pageSize=5, startRow=20, endRow=25, total=1017, pages=204, reasonable=false, pageSizeZero=false}, prePage=4, nextPage=6, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=8, navigateFirstPage1, navigateLastPage8, navigatepageNums=[1, 2, 3, 4, 5, 6, 7, 8]} System.out.println("分页信息"+pageInfo); model.addAttribute("pageInfo", pageInfo); return "empList"; } |
⑥empList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <table cellpadding="5" cellspacing="0" border="1"> <tr> <th>id</th> <th>employeeName</th> <th>gender</th> </tr> <c:forEach items="${ pageInfo.list}" var="item" > <tr> <td>${item.id} </td> <td>${item.employeeName}</td> <td>${item.gender }</td> </tr> </c:forEach> <tr> <td colspan="3"> <a href="./getEmpsByPage?page=1">首页</a> <a href="./getEmpsByPage?page=${pageInfo.prePage}">上一页</a> <c:forEach items="${pageInfo.navigatepageNums}" var="pageNum"> <c:if test="${pageInfo.pageNum==pageNum}"> <b>${pageNum}</b> </c:if> <c:if test="${pageInfo.pageNum!=pageNum}"> <a href="./getEmpsByPage?page=${pageNum}">${pageNum}</a> </c:if> </c:forEach> <a href="./getEmpsByPage?page=${pageInfo.nextPage}">下一页</a> <a href="./getEmpsByPage?page=${pageInfo.pages}">末页</a> </td> </tr> </table> </body> </html> |
监听器
监听对象:
监听事件:
触发行为:
pageContext–>request–>session–>application
八个监听器
按照监听对象分为三类:
1.监听ServletContextListener事件的
①ServletContextListener
作用:监听ServletContext生命周期(从创建到销毁的过程(服务器启动创建–服务器停止销毁))
①ServletContextAttributeListener
作用:监听ServletContext域中属性变化
2.监听HttpSession事件的
①HttpSessionListener
作用:监听session的生命周期(session是第一次使用的时候创建;session超时销毁,session手动销毁)
②HttpSessionAttributeListener
作用:监听HttpSession域中属性变化
③HttpSessionActivationListener
作用:监听某个对象随着HttpSession活化钝化的
④HttpSessionBindingListener
作用:监听某个对象保存(绑定)到session中和从session中移除(解绑)
3.监听ServletRequest事件的
①ServletRequestListener
作用:监听request对象的生命周期(请求进来创建新的request对象保存请求的详细信息,请求完成销毁request对象)
②ServletRequestAttributeListener
作用:监听request域中的属性变化
按照功能:
1.监听生命周期
ServletContextListener
//ServletContext销毁(服务器停止)调用
void contextDestroyed(ServletContextEvent sce)
//ServletContext初始化(服务器启动)调用
void contextInitialized(ServletContextEvent sce)
HttpSessionListener
//session创建调用(第一次使用session)
void sessionCreated(HttpSessionEvent se)
//session超时失效,手动设置失效时调用
void sessionDestroyed(HttpSessionEvent se)
ServletRequestListener
//request销毁调用(当次请求结束)
void requestDestroyed(ServletRequestEvent sre)
//request初始化(发起一次新请求)
void requestInitialized(ServletRequestEvent sre)
2.监听域中属性变化
ServletContextAttributeListener
HttpSessionAttributeListener
ServletRequestAttributeListener
// 添加属性
void attributeAdded(ServletContextAttributeEvent event)
void attributeRemoved(ServletContextAttributeEvent event)
void attributeReplaced(ServletContextAttributeEvent event)
3.session独有的监听器
HttpSessionActivationListener
监听某个对象随着HttpSession活化
void sessionDidActivate(HttpSessionEvent se)
监听某个对象随着HttpSession钝化
void sessionWillPassivate(HttpSessionEvent se)
HttpSessionBindingListener
监听某个对象在session中绑定进来了(保存进来)
void valueBound(HttpSessionBindingEvent event)
监听某个对象在session中解绑(移除)
void valueUnbound(HttpSessionBindingEvent event)
监听器如何工作
①写接口实现监听器
②在web.xml注册监听器
ServletContextListener监听ServletContext示例
①编写实现ServletContextListener的java类
package com.wuxinzhe.listener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class MyListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { // TODO Auto-generated method stub System.out.println("contextInitialized"+sce.getServletContext()); } @Override public void contextDestroyed(ServletContextEvent sce) { // TODO Auto-generated method stub System.out.println("contextDestroyed"+sce.getServletContext()); } } |
②在web.xml中注册监听器
<listener> <listener-class>com.wuxinzhe.listener.MyListener</listener-class> </listener> |
session的生命周期
1.创建
第一次使用的时候创建
第一次访问jsp内容,因为session对象在jsp创建
package com.wuxinzhe.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebServlet("/hahasession") public class SessionServlet extends HttpServlet { private static final long serialVersionUID = 1L; public SessionServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String string = request.getParameter("param"); HttpSession httpSession = request.getSession(); // 获取session if(string.equals("getsession")) { response.getWriter().write(httpSession.getId()); }else if(string.equals("invalidatesession")) { // 销毁session httpSession.invalidate(); response.getWriter().write("session invalidate"); } } } |
Servlet中获取和销毁session
package com.wuxinzhe.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebServlet("/hahasession") public class SessionServlet extends HttpServlet { private static final long serialVersionUID = 1L; public SessionServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String string = request.getParameter("param"); HttpSession httpSession = request.getSession(); // 获取session if(string.equals("getsession")) { response.getWriter().write(httpSession.getId()); }else if(string.equals("invalidatesession")) { // 销毁session httpSession.invalidate(); response.getWriter().write("session invalidate"); } } } |
HttpSessionListener,HttpSessionAttributeListener测试示例
MySessionLifeCycleListener监听器
package com.wuxinzhe.listener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class MySessionLifeCycleListener implements HttpSessionListener,HttpSessionAttributeListener { @Override public void sessionCreated(HttpSessionEvent arg0) { System.out.println("sessionCreated"+arg0.getSession().getId()); } @Override public void sessionDestroyed(HttpSessionEvent arg0) { System.out.println("sessionDestroyed"+arg0.getSession().getId()); } @Override public void attributeAdded(HttpSessionBindingEvent arg0) { System.out.println("session属性添加"); System.out.println("session属性名" + arg0.getName()); System.out.println("session属性值" + arg0.getValue()); } @Override public void attributeRemoved(HttpSessionBindingEvent arg0) { System.out.println("session属性移除"); System.out.println("session属性名" + arg0.getName()); System.out.println("session属性值" + arg0.getValue()); } @Override public void attributeReplaced(HttpSessionBindingEvent arg0) { System.out.println("session属性值替换"); System.out.println("session属性名" + arg0.getName()); System.out.println("session属性过去的值" + arg0.getValue()); System.out.println("session重设后的值"+arg0.getSession().getAttribute(arg0.getName())); } } |
session.jsp测试检查session的变化
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <% session.setAttribute("username", "admin"); Thread.sleep(3000); session.setAttribute("username", "admin2"); Thread.sleep(3000); session.removeAttribute("username"); %> </body> </html> |
HttpSessionActivationListener示例
钝化sessionWillPassivate:内存的数据写入到硬盘上的过程
活化sessionDidActivat:与钝化相反,就是将硬盘的数据恢复到内存中
package com.wuxinzhe.bean; import java.io.Serializable; import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import javax.servlet.http.HttpSessionEvent; /** * 实现序列化接口 *不需要在web.xml中注册的监听器 *HttpSessionActivationListener,HttpSessionBindingListener **/ public class User implements Serializable, HttpSessionActivationListener,HttpSessionBindingListener { @Override public void sessionDidActivate(HttpSessionEvent arg0) { System.out.println(this + "活化"); } @Override public void sessionWillPassivate(HttpSessionEvent arg0) { System.out.println(this + "钝化"); } //检测这个对象绑定到session中 @Override public void valueBound(HttpSessionBindingEvent arg0) { } //检测这个对象从session解绑(移除) @Override public void valueUnbound(HttpSessionBindingEvent arg0) { } } |
测试session中的对象钝化活化绑定解绑
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="com.wuxinzhe.bean.User"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <% User user = new User(); session.setAttribute("user", user); %> </body> </html> |
监听器一些使用场景
ServletContextListener:监听服务器启动停止
服务器启动:以后推荐些绝对路径,可以初始化一个项目根路径
服务器停止:做一些善后清理工作
HttpSessionBindingListener
用户在线统计