SSM常见题型
SpringBean的作用域
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- ★bean的作用域
可以通过scope属性来指定bean的作用域
-singleton:默认值。当IOC容器一创建就会创建bean的实例,而且是单例的,每次得到的都是同一个
-prototype:原型的。当IOC容器一创建不再实例化该bean,每次调用getBean方法时再实例化该bean,而且每调用一次创建一个对象
-request:每次请求实例化一个bean
-session:在一次会话中共享一个bean
-->
<bean id="book" class="com.atguigu.spring.beans.Book" scope="prototype">
<property name="id" value="8"></property>
<property name="title" value="红高粱"></property>
<property name="author" value="莫言"></property>
<property name="price" value="10.00"></property>
<property name="sales" value="800"></property>
</bean>
</beans>
Spring支持的常用数据库事务传播属性和事务隔离级别
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为。
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
/**
* @Transactional注解
* 该注解可以添加到类上,也可以添加到方法上
* 如果添加到类上,那么类中所有的方法都添加上了事务
* 如果添加到方法上,只有添加了该注解的方法才添加了事务
*/
//@Transactional
@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService {
@Autowired
private BookShopDao bookShopDao;
//1.请简单介绍Spring支持的常用数据库事务传播属性和事务隔离级别?
/**
* 事务的属性:
* 1.★propagation:用来设置事务的传播行为
* 事务的传播行为:一个方法运行在了一个开启了事务的方法中时,当前方法是使用原来的事务还是开启一个新的事务
* -Propagation.REQUIRED:默认值,使用原来的事务
* -Propagation.REQUIRES_NEW:将原来的事务挂起,开启一个新的事务
* 2.★isolation:用来设置事务的隔离级别
* -Isolation.REPEATABLE_READ:可重复读,MySQL默认的隔离级别
* -Isolation.READ_COMMITTED:读已提交,Oracle默认的隔离级别,开发时通常使用的隔离级别
*/
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED)
@Override
public void purchase(int userId, String isbn) {
//1.获取要买的图书的价格
double bookPrice = bookShopDao.getBookPriceByIsbn(isbn);
// System.out.println(bookPrice);
//2.更新图书的库存
bookShopDao.updateBookStock(isbn);
//3.更新用户的余额
bookShopDao.updateAccountBalance(userId, bookPrice);
// double bookPriceByIsbn = bookShopDao.getBookPriceByIsbn(isbn);
// System.out.println(bookPriceByIsbn);
}
}
@Service("cashier")
public class CashierImpl implements Cashier {
@Autowired
private BookShopService bookShopService;
@Transactional
@Override
public void checkout(int userId, List<String> isbns) {
for (String isbn : isbns) {
//调用BookShopService中买东西的方法
bookShopService.purchase(userId, isbn);
}
}
}
事务并发引起的问题以及如何避免
1,脏读(dirty read)
A事务读取B事务尚未提交的更改数据,并在这个数据基础上操作。如果B事务回滚,那么A事务读到的数据根本不是合法的,称为脏读。在oracle中,由于有version控制,不会出现脏读。
2,不可重复读(unrepeatable read)
A事务读取了B事务已经提交的**更改(或删除)**数据。比如A事务第一次读取数据,然后B事务更改该数据并提交,A事务再次读取数据,两次读取的数据不一样。
3,幻读(phantom read)
A事务读取了B事务已经提交的新增数据。**注意和不可重复读的区别,这里是新增,不可重复读是更改(或删除)。**这两种情况对策是不一样的,对于不可重复读,只需要采取行级锁防止该记录数据被更改或删除,然而对于幻读必须加表级锁,防止在这个表中新增一条数据。
4,第一类丢失更新
A事务撤销时,把已提交的B事务的数据覆盖掉。这种错误会造成非常严重的后果。
5,第二类丢失更新
A事务提交时,把已提交的B事务的数据覆盖掉。这种错误会造成非常严重的后果。
更新丢失——mysql所有事务隔离级别在数据库层面均可避免。
脏读——READ-COMMITTED事务隔离级别以上可以避免。(未提交的事务修改的数据也能读取到而引起的错误数据,RC级别以上可避免)
不可重复读——REPETABLE-READ事务隔离级别以上可避免。(加锁后读取的数据会因为其他事务操作而变化,RR级别以上可避免)
幻读——SERIALIZABLE事务隔离级别可避免。
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
Read uncommitted(读未提交) | √ | √ | √ |
Read committed(读已提交) | × | √ | √ |
Repeatable read(可重复读) | × | × | √ |
Serializable(串行化) | × | × | × |
隔离级别 描述
READ UNCOMMITTED(读未提交数据) 允许事务读取未被其他事务提交的变更,脏读、不可重复读和幻读的问题都会出现
READ COMMITED(读已提交数据) 只允许事务读取已经被其他事务提交的变更,可以避免脏读,但不可重复读和幻读问题仍然会出现
REPEATABLE READ(可重复读) 确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但幻读的问题依然存在
SERIALIZABLE(串行化) 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,所有并发问题都可以避免,但性能十分低
Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE.
Oracle 默认的事务隔离级别为: READ COMMITED
Mysql 支持 4 中事务隔离级别.
Mysql 默认的事务隔离级别为: REPEATABLE READ
共享锁和排它锁
为了解决并发问题,数据库系统引入锁机制。
基本的封锁类型有两种: 排它锁(Exclusive locks 简记为X锁) 和 共享锁(Share locks 简记为S锁)。
- 排它锁又称为写锁。若事务T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。这就保证了其它事务在T释放A上的锁之前不能再读取和修改A。
- 共享锁又称为读锁。若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其它事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这就保证了其它事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
SpringMVC中如何解决POST请求中文乱码问题GET的又如何处理呢
@Controller
public class SpringMVCHandler {
public static final String SUCCESS="success";
//1.SpringMVC中如何解决POST请求中文乱码问题,GET的又如何处理呢
/*
* ★测试入参为POJO
* Spring MVC会按请求参数名和 POJO属性名进行自动匹配,
* 自动为该对象填充属性值。支持级联属性
*/
@RequestMapping("/testPOJO")
public String testPOJO(Employee employee) {
System.out.println("员工的信息是:"+employee);
return SUCCESS;
}
}
<!--web.xml过滤器 解决POST请求中文乱码问题-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--Tomcat/server.xml 解决GET请求中文乱码问题 65行-->
<Connector URIEncoding="UTF-8"></Connector>
SpringMVC的工作流程
@Controller
public class SpringMVCHandler {
public static final String SUCCESS="success";
//1.简单的谈一下SpringMVC的工作流程
//处理模型数据方式一:将方法的返回值设置为ModelAndView
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
//1.创建ModelAndView对象
ModelAndView mav = new ModelAndView();
//2.设置模型数据,最终会放到request域中
mav.addObject("user", "admin");
//3.设置视图
mav.setViewName("success");
return mav;
}
/*
* ★处理模型数据方式二:方法的返回值仍是String类型,在方法的入参中传入Map、Model或者ModelMap
* 不管将处理器方法的返回值设置为ModelAndView还是在方法的入参中传入Map、Model或者ModelMap,
* SpringMVC都会转换为一个ModelAndView对象
*/
@RequestMapping("/testMap")
public String testMap(Map<String , Object> map) {
//向Map中添加模型数据,最终会自动放到request域中
map.put("user", new Employee(1, "韩总", "hybing@atguigu.com", new Department(101, "教学部")));
return SUCCESS;
}
}
- 用户发起一个request请求,这个请求将会首先到达前端控制器DispatcherServlet中,由DispatcherServlet统一调度其他组件的使用;
- DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器,由此得知,这个请求该由哪一个Controller来进行处理(并未调用Controller);
- 处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。DispatcherServlet根据处理器Handler获取HandlerAdapter处理器适配器,执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作,告诉处理器适配器应该要去执行哪个Controller;
- HandlerAdapter处理器适配器去执行Handler(Controller,也叫页面控制器),即我们的Controller,然后Handler处理器返回ModelAndView(数据和视图)给HandlerAdapter;
- HandlerAdapter再将ModelAndView(数据和视图)返回给DispatcherServlet;
- DispatcherServlet将ModelAndView交给ViewReslover视图解析器请求进行视图解析,ViewReslover解析后返回具体View,然后返回真正的视图;
- 视图渲染, DispatcherServlet将模型数据填充到request域中,对View进行渲染视图(即将模型数据model填充至视图中;
- DispatcherServlet将结果响应给用户;
MyBatis中当实体类中的属性名和表中的字段名不一样
class MyBatisTest {
//1.MyBatis中当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
/*
* 解决方案:
* 1.写sql语句时起别名
* 2.在MyBatis的全局配置文件中开启驼峰命名规则
* 3.在Mapper映射文件中使用resultMap来自定义映射规则
*/
@Test
void testGetEmployee() throws IOException {
//1.创建SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取sqlSession,sqlSession就相当于JDBC中的connection
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
//3.获取Mapper对象
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
//4.调用EmployeeMapper中获取Employee的方法
Employee employee = mapper.getEmployeeById(1);
System.out.println(employee);
} finally {
//5.关闭sqlSession
sqlSession.close();
}
}
}
<!--mybatis-config.xml-->
<settings>
<!-- 开启驼峰命名规则 ,可以将数据库中的下划线映射为驼峰命名
例如:last_name可以映射为lastName
-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>