创建项目命名SSM
创建模块命名为MyBatis_HelloWorld
打包方式设置为jar
jar
引入依赖
org.mybatis
mybatis
3.5.7
junit
junit
4.12
test
mysql
mysql-connector-java
5.1.3
打开sqlyog创建数据库ssm并创建表t_user
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`password` varchar(20) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`gender` char(1) DEFAULT NULL,
`emall` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
private Integer id;
private String username;
private String password;
private Integer age;
private String gender;
private String email;
public User() {
}
public User(Integer id, String username, String password, Integer age, String gender, String email) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
this.gender = gender;
this.email = email;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类。
public interface UserMapper {
int insertUser();
}
在resources目录下创建mappers目录在mappers目录下创建UserMapper.xml配置文件
insert into t_user values (null,'admin','123456',23,'男','[email protected]')
如果报错的话就是没有把mybatis添加到库里面
package com.seven.mybatis;
import com.seven.mybatis.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
/**
* @name hk
* @time 2022-08-31-17:59
*/
public class MyBatisTest {
@Test
public void test() throws IOException {
//获取核心配置文件的输入流
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory对象
SqlSessionFactory build = sqlSessionFactoryBuilder.build(is);
//获取SQL的会话对象SqlSession,是MyBatis提供的操作数据库的对象
SqlSession sqlSession = build.openSession();
//获取UserMapper代理实现类对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.insertUser();
System.out.println(i);
sqlSession.close();
}
}
查看sqlyog是否添加成功
2022/8/31 20:11:00
//获取核心配置文件的输入流
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory对象
SqlSessionFactory build = sqlSessionFactoryBuilder.build(is);
//获取SQL的会话对象SqlSession()不会自动提交事务,是MyBatis提供的操作数据库的对象
//SqlSession sqlSession = build.openSession();
//获取SQL的会话对象SqlSession(true)会自动提交事务,是MyBatis提供的操作数据库的对象
SqlSession sqlSession = build.openSession(true);
//获取UserMapper代理实现类对象
//UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//提供sql以及唯一标识找到sql并执行,唯一标识是namespace.sqlId
int i = sqlSession.insert("com.seven.mybatis.mapper.UserMapper.insertUser");
//int i = mapper.insertUser();
System.out.println(i);
//提交事务
//sqlSession.commit();
sqlSession.close();
log4j
log4j
1.2.17
package com.seven.mybatis.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
/**
* @name hk
* @time 2022-09-01-15:59
*/
public class SqlSessionUtil {
public static SqlSession getSqlSession(){
SqlSession sqlSession =null;
try {
//获取核心配置文件的输入流
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory对象
SqlSessionFactory build = builder.build(is);
//获取SqlSession对象
sqlSession = build.openSession(true);
} catch (IOException e) {
e.printStackTrace();
}
return sqlSession;
}
}
update t_user set gender = '女' where id =2
public void upData(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.upDateUser();
sqlSession.close();
}
delete from t_user where id =3
public void delete(){
SqlSession session = SqlSessionUtil.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.deleteUser();
session.close();
}
查询单行信息
public void getUserById(){
SqlSession session = SqlSessionUtil.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User userById = mapper.getUserById();
System.out.println(userById);
session.close();
}
查询全部信息
public void getAllUser(){
SqlSession session = SqlSessionUtil.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List allUser = mapper.getAllUser();
allUser.forEach(System.out::println);
session.close();
}
typeAliases标签
environments标签
transactionManager标签
properties标签
dateSource标签
property标签
mapper标签
不好意思!开学比较忙马上更新9.22
若mapper接口方法的参数为多个字面量类型
此时mybatis会把参数放到map集合中,以两种方式存储
因此只需要通过#{}和${}访问map集合的键,就可以获取相对应的值,一定要注意${}的单引号问题。
若mapper接口方法的参数为map集合类型的参数
只需通过#{}和${}访问map集合的键,就可以获得相对应的值,一定要注意${}的单引号问题。
若mapper接口方法的参数为实体类类型的参数
只需要通过#{}和${}访问实体类中的属性名,就可以获得相对应的属性值,一定要注意${}的单引号问题。
可以在mapper接口方法的参数上设置@param注解
此时mybatis会把这些参数放到map中,以两种方式进行存储。
只需要通过#{}和${}访问map集合的键,就可以获取相应的值,一定要注意${}的单引号问题
根据ID获取User对象
/**
* 通过id获取用户数据
* @param id
* @return
*/
User getUserById(@Param("id") Integer id);
查询所有t_user表的数据
/**
* 获取所有用户数据
* @return
*
* 如果获取表中所有的数据需要list集合接收
*
*/
List getAllUser();
/**
* 获取所有用户数据
* @return
* 如果用实体类对象接收则会报错 报错信息为
* org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null)
* to be returned by selectOne(), but found: 5
* 期望返回一个结果发现返回5条数据
*/
User getAllUser();
利用map集合获取数据
/**
* 用map集合获取数据
* @param id
* @return
* 以字段名为键 以字段的值为值 如果某一字段值为null 则不会放到map集合中
*/
Map getAllUserByMap(@Param("id") Integer id);
利用map集合获取全部数据
/**
* 用map集合获取数据mapKey为键 以获取到的数据为值
* @return
*/
@MapKey("id")
Map getAllUserByMaps();
2022年9月25日 星期日 23:20:55
模糊查询
/**
* 测试模糊查询
* @return
*/
List testMohu(@Param("mohu") String mohu);
批量删除
/**
* 批量删除
* @param ids
* @return
*/
int deleteMore(@Param("ids") String ids);
delete from t_user where id in (${ids})
动态设置表名
/**
* 动态设置表名,查询所有的用户信息
* @param tableName
* @return
*/
List getAllUser(@Param("tableName") String tableName);
获取自增的主键
/**
* 添加用户信息
* @param user
* @return
*/
int insertUser(User user);
insert into t_user values(null,#{username},#{password},#{age},#{sex})
resultMup处理字段和属性
若字段名和实体类中的属性名不一致, 但是字段名符合数据库的规则(使用_) ,实体类中的属性名符合 Java 的规则(使用驼峰)此时也可通过以下两种方式处理字段名和实体类中的属性的映射关系a> 可以通过为字段起别名的方式,保证和实体类中的属性名保持一致b> 可以在 MyBatis 的核心配置文件中设置一个全局配置信息 mapUnderscoreToCamelCase ,可以在查询表中数据时,自动将 _ 类型的字段名转换为驼峰例如:字段名 user_name ,设置了 mapUnderscoreToCamelCase ,此时字段名就会转换为userName
查询员工信息及员工所对应的部门信息
/**
* 通过分步查询查询员工信息
* @param eid
* @return
*/
Emp getEmpByStep(@Param("eid") int eid);
/**
* 分步查询的第二步: 根据员工所对应的did查询部门信息
* @param did
* @return
*/
Dept getEmpDeptByStep(@Param("did") int did);
一步一步查先查第一步查到emp表的信息然后mybatis会自动调用 select标签的接口并把查询到的数据通过column 内的遍历传进getEmpDeptByStep的参数中执行第二个查询语句并返回dept类型的数据(大概就是这个意思,有错误欢迎告诉我我会及时更正 一起加油!!)
分步查询的优点:可以实现延迟加载但是必须在 核心配置文件中 设置全局配置信息:lazyLoadingEnabled :延迟加载的全局开关。当开启时,所有关联对象都会延迟加载aggressiveLazyLoading :当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql 。此时可通过 association 和collection中的 fetchType 属性设置当前的分步查询是否使用延迟加载, fetchType="lazy( 延迟加载)|eager( 立即加载 )"
MyBatis框架的动态SQL技术是一种根据特定条件动态拼接SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。
if标签可通过test属性的表达式进行判断,若判断表达式结果为true,则标签中的内容会执行;反之标签中的内容不会执行。
休息休息QAQ 2022年10月6日18:11:11
相当于java中的if...else if...else when至少设置一个,otherwise最多设置一个
循环添加
insert into t_emp values
(null,#{emp.empName},#{emp.age},#{emp.gender},null)
循环删除
#{empId}
-->
delete from t_emp where
emp_id = #{empId}
可以记录一段sql,在需要用的地方使用include标签进行引用
emp_id,emp_name,age,gender,dept_id
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
使一级缓存失效的四种情况:
看不懂的话 跳过
org.mybatis.caches
mybatis-ehcache
1.2.1
ch.qos.logback
logback-classic
1.2.3
[%d{HH:mm:ss.SSS}][%-5level][%thread][%logger]
[%msg]%n
麻烦吗? 兄弟们 麻烦就接着看 最牛逼的要来了 MyBatis的逆向工程YYDS
org.mybatis
mybatis
3.5.7
junit
junit
4.12
test
log4j
log4j
1.2.17
mysql
mysql-connector-java
8.0.16
com.github.pagehelper
pagehelper
5.2.0
org.mybatis.generator
mybatis-generator-maven-plugin
1.3.0
org.mybatis.generator
mybatis-generator-core
1.3.2
mysql
mysql-connector-java
8.0.16
dependency>
com.github.pagehelper pagehelper
5.2.0
- 在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能pageNum:当前页的页码 pageSize:每页显示的条数
在查询获取 list 集合之后 使用 PageInfopageInfo = new PageInfo<>(List navigatePages)获取分页相关 数据 list:分页之后的数据 navigatePages:导航分页的页码数list, int - 分页相关数据
PageInfo{pageNum=8, pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8,list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30,pages=8, reasonable=false, pageSizeZero=false},prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true,hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8,navigatepageNums=[4, 5, 6, 7, 8]}pageNum :当前页的页码pageSize :每页显示的条数size :当前页显示的真实条数total :总记录数pages :总页数prePage :上一页的页码nextPage :下一页的页码isFirstPage/isLastPage :是否为第一页 / 最后一页hasPreviousPage/hasNextPage :是否存在上一页 / 下一页navigatePages :导航分页的页码数navigatepageNums :导航分页的页码, [1,2,3,4,5]
Mybatis完结撒花 spring 已经看完 准备复习+笔记 2022.10.15/23:59:15
- Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用Spring 框架来创建性能好、易于测试、可重用的代码。
- Spring 框架是一个开源的 Java 平台,它最初是由 Rod Johnson 编写的,并且于 2003 年 6 月首次在 Apache 2.0 许可下发布。
- Spring 是轻量级的框架,其基础版本只有 2 MB 左右的大小。
- Spring 框架的核心特性是可以用于开发任何 Java 应用程序,但是在 Java EE 平台上构建 web 应用程序是需要扩展的。 Spring 框架的目标是使 J2EE 开发变得更容易使用,通过启用基于 POJO编程模型来促进良好的编程实践。
项目列表:https://spring.io/projects
- 非侵入式:使用 Spring Framework 开发应用程序时,Spring 对应用程序本身的结构影响非常小。对领域模型可以做到零污染;对功能性组件也只需要使用几个简单的注解进行标记,完全不会破坏原有结构,反而能将组件结构进一步简化。这就使得基于 Spring Framework 开发应用程序时结构清晰、简洁优雅。
- 控制反转:IOC——Inversion of Control,翻转资源获取方向。把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。(重点)
- 面向切面编程:AOP——Aspect Oriented Programming,在不修改源代码的基础上增强代码功能。(重点)
- 容器:Spring IOC 是一个容器,因为它包含并且管理组件对象的生命周期。组件享受到了容器化的管理,替程序员屏蔽了组件创建过程中的大量细节,极大的降低了使用门槛,大幅度提高了开发效率。(理解)
- 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用 XML和 Java 注解组合这些对象。这使得我们可以基于一个个功能明确、边界清晰的组件有条不紊的搭建超大型复杂应用系统。
- 声明式:很多以前需要编写代码才能实现的功能,现在只需要声明需求即可由框架代为实现。
- 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。而且 Spring 旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在 Spring Framework 的基础上全部使用 Spring 来实现。
功能模块
|
功能介绍
|
---|---|
Core Container
|
核心容器,在 Spring 环境下使用任何功能都必须基于 IOC 容器。
|
AOP&Aspects
|
面向切面编程。
|
Testing
|
提供了对 junit 或 TestNG 测试框架的整合。
|
Data Access/Integration
|
提供了对数据访问 / 集成的功能。
|
Spring MVC
|
提供了面向 Web 应用程序的集成功能。
|
IOC:Inversion of Control,翻译过来是反转控制。
IOC容器在Spring中的实现
类型名
|
简介
|
---|---|
ClassPathXmlApplicationContext
|
通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
|
FileSystemXmlApplicationContext
|
通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
|
ConfigurableApplicationContext
|
ApplicationContext 的子接口,包含一些扩展方法
refresh() 和 close() ,让 ApplicationContext 具有启动、
关闭和刷新上下文的能力。
|
WebApplicationContext
|
专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对
象,并将对象引入存入 ServletContext 域中。
|
org.springframework
spring-context
5.3.1
junit
junit
4.12 test
public class HelloWorld {
public void sayHello(){
System.out.println("hello,spring");
}
}
@Test
public void test(){
//获取IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取IOC容器中的bean
HelloWorld helloworld = (HelloWorld) ioc.getBean("helloworld");
helloworld.sayHello();
}
注意
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'helloworld' defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failedto instantiate [com.atguigu.spring.bean.HelloWorld]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.atguigu.spring.bean.HelloWorld.()
方式一:根据id获取
由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。 上个实验中我们使用的就是这种方式。
@Test
public void test(){
//获取IOC容器
ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
//获取IOC容器中的bean对象
HelloSpring helloSpring =
(HelloSpring) ioc.getBean(HelloSpring.class);
helloSpring.seyHello();
}
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.atguigu.spring.bean.HelloWorld' available: expected single matching bean but found 2: helloworldOne,helloworldTwo
可以,前提是 bean 唯一
不行,因为 bean 不唯一
public class Student {
private String name;
private int age;
private Dept dept;
public Student(String name, int age, Dept dept) {
this.name = name;
this.age = age;
this.dept = dept;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", dept=" + dept +
'}';
}
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Test
public void test() {
//bean.xml为配置注入的spring配置文件
ApplicationContext ioc = new ClassPathXmlApplicationContext("been.xml");
Student student = ioc.getBean("student", Student.class);
System.out.println(student);
}
2022.10.16 14:54:25 休息休息
public Student(Integer id,String name,Integer age,String sex){
this.id=id;
this.name=name;
this.age=age;
this.sex=sex;
}
@Test
public void testDIBySet(){
ApplicationContextac=
new ClassPathXmlApplicationContext("spring-di.xml");
StudentstudentOne = ac.getBean("studentTwo",Student.class);
System.out.println(studentOne);
}
什么是字面量?int a = 10;声明一个变量 a ,初始化为 10 ,此时 a 就不代表字母 a 了,而是作为一个变量的名字。当我们引用 a的时候,我们实际上拿到的值是 10 。而如果 a 是带引号的: 'a' ,那么它现在不是一个变量,它就是代表 a 这个字母本身,这就是字面量。所以字面量没有引申含义,就是我们看到的这个数据本身。
注意:
以上写法,为 name 所赋的值是字符串 null
public class Clazz {
private Integer clazzId;
private String clazzName;
public Integer getClazzId() {
return clazzId;
}
public void setClazzId(Integer clazzId) {
this.clazzId = clazzId;
}
public String getClazzName() {
return clazzName;
}
public void setClazzName(String clazzName) {
this.clazzName = clazzName;
}
@Override
public String toString() {
return "Clazz{" +
"clazzId=" + clazzId +
", clazzName='" + clazzName + '\'' +
'}';
}
public Clazz() {
}
public Clazz(Integer clazzId, String clazzName) {
this.clazzId = clazzId;
this.clazzName = clazzName;
}
}
private Clazz clazz;
public Clazz getClazz() {
return clazz;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
如果错把 ref 属性写成了 value 属性,会抛出异常: Caused by: java.lang.IllegalStateException:Cannot convert value of type 'java.lang.String' to required type'com.atguigu.spring.bean.Clazz' for property 'clazz': no matching editors or conversionstrategy found意思是不能把 String 类型转换成我们要的 Clazz 类型,说明我们使用 value 属性时, Spring 只把这个属性看做一个普通的字符串,不会认为这是一个bean 的 id ,更不会根据它去找到 bean 来赋值
private String[] hobbies;
public String[] getHobbies() {
return hobbies;
}
public void setHobbies(String[] hobbies) {
this.hobbies = hobbies;
}
抽烟
喝酒
烫头
@Test
public void testScope(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-scope.xml");
Student bean = ioc.getBean("studentFour",Student.class);
// 我重写了tostring方法所以可以直接输出
System.out.println(bean);
}
mysql
mysql-connector-java
8.0.16
com.alibaba
druid
1.0.31
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:13306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=asd1230.
这是配置自己的数据库信息
@Test
public void testConn() throws SQLException {
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-datasource.xml");
DruidDataSource bean = ioc.getBean(DruidDataSource.class);
System.out.println(bean.getConnection());
}
注意xml名字改成自己写的,如果连接失败看一下是不是自己的信息填错了或者数据库开了没
取值
|
含义 |
创建对象的时机
|
---|---|---|
singleton (默认)
|
在 IOC 容器中,这个 bean 的对象始终为单实例
|
IOC 容器初始化时
|
prototype
|
这个 bean 在 IOC 容器中有多个实例
|
获取 bean 时
|
取值
|
含义 |
---|---|
request
|
在一个请求范围内有效
|
session
|
在一个会话范围内有效
|
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
public User() {
}
public User(Integer id, String username, String password, Integer age) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
@Test
public void testScope(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-scope.xml");
Student bean = ioc.getBean(Student.class);
Student bean1 = ioc.getBean(Student.class);
System.out.println(bean);
System.out.println(bean1);
System.out.println(bean==bean1);
}
证明容器获取的同一个对象单例模式
如果把配置文件中scope的值更改为prototype则效果如下
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
public User() {
System.out.println("生命周期:1、创建对象");
}
public User(Integer id, String username, String password, Integer age) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
System.out.println("生命周期:2、依赖注入");
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public void initMethod(){
System.out.println("生命周期:3、初始化");
}
public void destroyMethod(){
System.out.println("生命周期:5、销毁");
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username =='" + username + '\'' +
", password =='" + password + '\'' +
", age=" + age +
'}';
}
}
注意其中的initMethod()和destroyMethod(),可以通过配置bean指定为初始化和销毁的方法
@Test
public void testLife(){
ClassPathXmlApplicationContext ac = new
ClassPathXmlApplicationContext("spring-lifecycle.xml");
User bean = ac.getBean(User.class);
System.out.println("生命周期:4、通过IOC容器获取bean并使用");
ac.close();
}
package com.atguigu.spring.process;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("☆☆☆" + beanName + " = " + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("★★★" + beanName + " = " + bean);
return bean;
}
}
创建类UserFactoryBean
public class UserFactoryBean implements FactoryBean {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class> getObjectType() {
return User.class;
}
}
@Test
public void testUserFactoryBean(){
//获取IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("springfactorybean.xml");
User user = (User) ac.getBean("user");
System.out.println(user);
}
自动装配:
根据指定的策略,在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void saveUser(){
userService.saveUser();
}
}
public interface UserService {
void saveUser();
}
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void saveUser() {
userDao.saveUser();
}
}
public interface UserDao {
void saveUser();
}
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("保存成功");
}
}
使用 bean 标签的 autowire 属性设置自动装配效果自动装配方式:byTypebyType :根据类型匹配 IOC 容器中的某个兼容类型的 bean ,为属性自动赋值若在 IOC 中,没有任何一个兼容类型的 bean 能够为属性赋值,则该属性不装配,即值为默认值 null若在 IOC 中,有多个兼容类型的 bean 能够为属性赋值,则抛出异常NoUniqueBeanDefinitionException
自动装配方式: byNamebyName :将自动装配的属性的属性名,作为 bean 的 id 在 IOC 容器中匹配相对应的 bean 进行赋值
测试
@Test
public void testAutoWireByXML(){
ApplicationContext ac = new ClassPathXmlApplicationContext("autowirexml.xml");
UserController userController = ac.getBean(UserController.class);
userController.saveUser();
}
理解为备注,具体功能为让框架检测到标注的位置,并执行注解的类型来执行。注解本身不能执行
spring需要通过扫描来查看注解的位置进而执行。
org.springframework
spring-context
5.3.1
junit
junit
4.12
test
通过查看源码我们得知,@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。
注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记
@Autowired注解(自动装配)
在成员变量上直接标记@Autowired注解即可完成自动装配,不需要提供setXxx()方法。以后我们在项目中的正式用法就是这样。
@Controller
public class UserController {
@Autowired
private UserService userService;
public void saveUser(){
userService.saveUser();
}
}
具体流程为在spring容器中搜索UserService的bean(当然要给UserService这个类加上注解不然会扫描不到)然后注入给当前标注的属性
横切关注点
Advice通知
AOP在特定的切入点上执行的增强处理,有before(前置)、after(后置)、afterReturning(最终)、afterThrowing(异常)、around(环绕)。
Aspect(切面)
通常是一个类,里面可以定义切入点和通知。
JoinPoint(连接点)
Pointcut(切入点)
目标对象(Target Object)
AOP代理(AOP Proxy)
动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。
AspectJ:本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终效果是动态的。weaver就是织入器。Spring只是借用了AspectJ中的注解。
org.springframework
spring-context
5.3.1
org.springframework
spring-aspects
5.3.1
junit
junit
4.12
test
public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
@Component
public class CalculatorPureImpl implements Calculator {
@Override
public int add(int i, int j) {
int result = i + j;
System.out.println("方法内部 result = " + result);
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
System.out.println("方法内部 result = " + result);
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
System.out.println("方法内部 result = " + result);
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
System.out.println("方法内部 result = " + result);
return result;
}
}
// @Aspect表示这个类是一个切面类
@Aspect
// @Component注解保证这个切面类能够放入IOC容器
@Component
public class LogAspect {
@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:" + methodName + ",参数:"+args);
}
@After("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->后置通知,方法名:" + methodName);
}
@AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = " result")
public void afterReturningMethod(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名:" + methodName + ",结果:"+result);
}
@AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = " ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->异常通知,方法名:" + methodName + ",异常:" + ex);
}
@Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
Object result = null;
try {
System.out.println("环绕通知-->目标对象方法执行之前");
//目标对象(连接点)方法的执行
result = joinPoint.proceed();
System.out.println("环绕通知-->目标对象方法返回值之后");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕通知-->目标对象方法出现异常时");
} finally {
System.out.println("环绕通知-->目标对象方法执行完毕");
}
return result;
}
}
@Pointcut("execution(* com.atguigu.aop.annotation.*.*(..))")
public void pointCut(){}
@Before("pointCut()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}
@Before("com.atguigu.aop.CommonPointCut.pointCut()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}
@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint){
//获取连接点的签名信息
String methodName = joinPoint.getSignature().getName();
//获取目标方法到的实参信息
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}
@AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*
(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
}
获取目标方法的异常
@AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*
(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}
@Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
Object result = null;
try {
System.out.println("环绕通知-->目标对象方法执行之前");
//目标方法的执行,目标方法的返回值一定要返回给外界调用者
result = joinPoint.proceed();
System.out.println("环绕通知-->目标对象方法返回值之后");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕通知-->目标对象方法出现异常时");
} finally {
System.out.println("环绕通知-->目标对象方法执行完毕");
}
return result;
}
使用@Order注解可以控制切面的优先级:
org.springframework
spring-context
5.3.1
org.springframework
spring-orm
5.3.1
org.springframework
spring-test
5.3.1
junit
junit
4.12
test
mysql
mysql-connector-java
8.0.16
com.alibaba
druid
1.0.31
jdbc.user=root
jdbc.password=atguigu
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.driver=com.mysql.cj.jdbc.Driver
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-jdbc.xml")
public class JDBCTemplateTest {
@Autowired
private JdbcTemplate jdbcTemplate;
}
@Test
//测试增删改功能
public void testUpdate(){
String sql = "insert into t_emp values(null,?,?,?)";
int result = jdbcTemplate.update(sql, "张三", 23, "男");
System.out.println(result);
}
Connection conn = ...;
try {
// 开启事务:关闭事务的自动提交
conn.setAutoCommit(false);
// 核心操作
// 提交事务
conn.commit();
}catch(Exception e){
// 回滚事务
conn.rollBack();
}finally{
// 释放数据库连接
conn.close();
}
org.springframework
spring-context
5.3.1
org.springframework
spring-orm
5.3.1
org.springframework
spring-test
5.3.1
junit
junit
4.12
test
mysql
mysql-connector-java
8.0.16
com.alibaba
druid
1.0.31
jdbc.user=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
CREATE TABLE `t_book` (
`book_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`book_name` varchar(20) DEFAULT NULL COMMENT '图书名称',
`price` int(11) DEFAULT NULL COMMENT '价格',
`stock` int(10) unsigned DEFAULT NULL COMMENT '库存(无符号)',
PRIMARY KEY (`book_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
insert into `t_book`(`book_id`,`book_name`,`price`,`stock`) values (1,'斗破苍
穹',80,100),(2,'斗罗大陆',50,100);
CREATE TABLE `t_user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` varchar(20) DEFAULT NULL COMMENT '用户名',
`balance` int(10) unsigned DEFAULT NULL COMMENT '余额(无符号)',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
insert into `t_user`(`user_id`,`username`,`balance`) values (1,'admin',50);
@Controller
public class BookController {
@Autowired
private BookService bookService;
public void buyBook(Integer bookId, Integer userId){
bookService.buyBook(bookId, userId);
}
}
public interface BookService {
void buyBook(Integer bookId, Integer userId);
}
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public void buyBook(Integer bookId, Integer userId) {
//查询图书的价格
Integer price = bookDao.getPriceByBookId(bookId);
//更新图书的库存
bookDao.updateStock(bookId);
//更新用户的余额
bookDao.updateBalance(userId, price);
}
}
public interface BookDao {
Integer getPriceByBookId(Integer bookId);
void updateStock(Integer bookId);
void updateBalance(Integer userId, Integer price);
}
创建实现类BookDaoImpl:
@Repository
public class BookDaoImpl implements BookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Integer getPriceByBookId(Integer bookId) {
String sql = "select price from t_book where book_id = ?";
return jdbcTemplate.queryForObject(sql, Integer.class, bookId);
}
@Override
public void updateStock(Integer bookId) {
String sql = "update t_book set stock = stock - 1 where book_id = ?";
jdbcTemplate.update(sql, bookId);
}
@Override
public void updateBalance(Integer userId, Integer price) {
String sql = "update t_user set balance = balance - ? where user_id =?";
jdbcTemplate.update(sql, price, userId);
}
}
介绍
使用方式
@Transactional(readOnly = true)
public void buyBook(Integer bookId, Integer userId) {
//查询图书的价格
Integer price = bookDao.getPriceByBookId(bookId);
//更新图书的库存
bookDao.updateStock(bookId);
//更新用户的余额
bookDao.updateBalance(userId, price);
//System.out.println(1/0);
}
@Transactional(timeout = 3)
public void buyBook(Integer bookId, Integer userId) {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//查询图书的价格
Integer price = bookDao.getPriceByBookId(bookId);
//更新图书的库存
bookDao.updateStock(bookId);
//更新用户的余额
bookDao.updateBalance(userId, price);
//System.out.println(1/0);
}
可以通过@Transactional中相关属性设置回滚策略
rollbackFor属性:需要设置一个Class类型的对象
rollbackForClassName属性:需要设置一个字符串类型的全类名
noRollbackFor属性:需要设置一个Class类型的对象
rollbackFor属性:需要设置一个字符串类型的全类名
@Transactional(noRollbackFor = ArithmeticException.class)
//@Transactional(noRollbackForClassName = "java.lang.ArithmeticException")
public void buyBook(Integer bookId, Integer userId) {
//查询图书的价格
Integer price = bookDao.getPriceByBookId(bookId);
//更新图书的库存
bookDao.updateStock(bookId);
//更新用户的余额
bookDao.updateBalance(userId, price);
System.out.println(1/0);
}
@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化
创建接口CheckoutService:
public interface CheckoutService {
void checkout(Integer[] bookIds, Integer userId);
}
@Service
public class CheckoutServiceImpl implements CheckoutService {
@Autowired
private BookService bookService;
@Override
@Transactional
//一次购买多本图书
public void checkout(Integer[] bookIds, Integer userId) {
for (Integer bookId : bookIds) {
bookService.buyBook(bookId, userId);
}
}
}
@Autowired
private CheckoutService checkoutService;
public void checkout(Integer[] bookIds, Integer userId){
checkoutService.checkout(bookIds, userId);
}