没有加@Transactional注解。
异常未被正确捕获:默认情况下只在遇到RuntimeException及其子类时进行回滚,其他异常不回滚。
指定特定异常回滚
@Transactional(rollbackFor = { CustomException.class })
public void myTransactionalMethod() {
// 在这个方法中抛出CustomException异常时,事务将回滚
}
指定特定异常不回滚
@Transactional(noRollbackFor = { CustomException.class })
public void myTransactionalMethod() {
// 在这个方法中抛出CustomException异常时,事务将不会回滚
}
不通过代理对象调用(通过目标对象调用)。
事务方法是私有的或final的(动态代理需要继承)。
使用不支持事务的存储引擎。
ioc/aop/快速集成其他框架/扩展性
总而言之,使用 Spring 框架可以
OOP将业务封装为对象(对象的属性与行为/方法),横切关注点跨越了对象的边界(多个对象之间有共同的行为)
横切关注点:多个模块或组件共享的功能(方法),例如日志记录,事务管理,安全等
PersonServiceImpl target = new PersonServiceImpl();
// UserInterface接口的代理对象
// Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Object proxy = Proxy.newProxyInstance(PersonService.class.getClassLoader(), new Class[]{PersonService.class}, (proxy1, method, args1) -> {
System.out.println("before...");
Object result = method.invoke(target, args1);
System.out.println("after...");
return result;
});
PersonService userService = (PersonService) proxy;
userService.addPerson(new Person("张三三"));
PersonServiceImpl target = new PersonServiceImpl();
// 通过cglib技术 /ɪnˈhɑːnsə(r)/ 增强器
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonServiceImpl.class);
// 定义额外逻辑,也就是代理逻辑
enhancer.setCallbacks(new Callback[]{(MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("before...");
Object result = methodProxy.invoke(target, objects);
System.out.println("after...");
return result;
}});
// 动态代理所创建出来的UserService对象
PersonServiceImpl personService = (PersonServiceImpl) enhancer.create();
// 执行这个userService的test方法时,就会额外会执行一些其他逻辑
personService.addPerson(new Person("张三三"));
Bean包含可变状态(例如实例变量),那么就存在线程安全问题。
为了解决单例Bean的线程安全问题,可以采取以下几种方式:
ConcurrentHashMap
。synchronized
关键字或锁)来确保对共享状态的访问是互斥的。这样一次只能有一个线程访问该Bean,但可能会导致性能下降。@Scope("prototype")
作用域的Bean或使用@RequestScope
、@SessionScope
等与线程相关的作用域。ThreadLocal
来为每个线程提供独立的实例。这样每个线程都可以独立地访问和修改自己的实例,避免了线程安全问题。通过动态代理实现,方法前开启事务,方法结束后提交事物,发生异常时回归事物.
与数据库事务隔离界别一致
isolation /ˌaɪsəˈleɪʃn/ 隔离
@Transactional(isolation = Isolation.READ_COMMITTED)
public enum Isolation {
DEFAULT(-1),
READ_UNCOMMITTED(1),
READ_COMMITTED(2),
REPEATABLE_READ(4),
SERIALIZABLE(8);
private final int value;
private Isolation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
propagation /ˌprɒpə’ɡeɪʃ(ə)n/ 传播
@Transactional(propagation = Propagation.REQUIRED)
public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
private final int value;
private Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
测试代码见[Spring之事务的传播行为]
将请求映射到处理器类上或者处理器方法上
post请求:设置字符编码过滤器来实现
get请求:Spring MVC会使用URL编码来传输参数,可以在Controller中手动进行解码操作。
import java.net.URLDecoder;
...
@RequestMapping(value = "/example", method = RequestMethod.GET)
public String handleGetRequest(@RequestParam("param") String param) {
String decodedParam = URLDecoder.decode(param, "UTF-8");
// 处理解码后的参数
...
}
springboot中可以这么配置(基本都是默认配置)
# POST请求中文乱码处理
spring.http.encoding.force-request=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.force=true
# GET请求中文乱码处理
server.tomcat.uri-encoding=UTF-8
默认是的
并发问题
解决方案:
在springboot中的重定向
@RestController
public class HelloController {
@RequestMapping("/hello")
public Object hello(String name) {
String hello = "hello " + System.currentTimeMillis() + " " + name;
System.out.println(hello);
return hello;
}
}
@RestController
public class RedirectController {
@RequestMapping("/redirect0")
public RedirectView redirect0(RedirectAttributes redirectAttributes) {
// RedirectAttributes是Spring MVC提供的一个工具类,用于将参数添加到重定向URL中,类似于get请求
redirectAttributes.addAttribute("name", "张三");
RedirectView redirectView = new RedirectView();
redirectView.setUrl("hello");
System.out.println("redirect0");
return redirectView;
}
@RequestMapping("/redirect1")
public String redirect1(HttpServletResponse response, RedirectAttributes redirectAttributes) throws IOException {
// 参数转不过去
// redirectAttributes.addAttribute("name", "张三");
// response.sendRedirect("hello?name=lisi");
// 中文乱码
// response.sendRedirect("hello?name=李四");
response.sendRedirect("hello?name=" + URLEncoder.encode("李四", "utf-8"));
// 因为是浏览器实现的,会将此次请求执行完成,因此下面这行代码会执行
System.out.println("redirect1");
return "okk";
}
@RequestMapping("/redirect2")
public String redirect2() {
System.out.println("redirect2");
// @RestController 不会实现重定向的效果,使用@Controller可以
return "redirect0:hello";
}
}
@RestController
public class ForwardController {
@GetMapping("/forward0")
public ModelAndView forward0(String name) {
System.out.println("利用 ModelAndView 转发前 " + name);
ModelAndView modelAndView = new ModelAndView();
// 会将name继续传递给hello请求
modelAndView.setViewName("forward:hello");
System.out.println("利用 ModelAndView 转发后 " + name);
return modelAndView;
}
@RequestMapping("/forward1")
public String forward1(String name, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
System.out.println("利用 request 转发前 " + name);
// 会将name继续传递给hello请求
request.getRequestDispatcher("hello").forward(request, response);
System.out.println("利用 request 转发后 " + name);
return "okk";
}
@GetMapping("/forward2")
public String forward2() {
// 只能在@Controller下使用,@RestController当正常请求返回字符串
return "forward:hello";
}
}
@Component
@WebFilter(urlPatterns = {"/*"})
public class CustomFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 在请求进入容器后 servlet前
System.out.println("CustomFilter doFilter 前");
// 放行请求
chain.doFilter(request, response);
// 请求离开servlet后
System.out.println("CustomFilter doFilter 后");
}
}
@Controller
注解来标识控制器类,使用@RequestMapping
注解来定义请求映射等。struts.xml
文件来定义控制器、拦截器、结果视图等。MyBatis是一个开源的Java持久层框架,它简化了与关系型数据库的交互过程,通过将SQL语句与Java代码进行解耦,提供了一种优雅而灵活的方式来进行数据库访问。
以下是我对MyBatis的一些理解:
总的来说,MyBatis是一个功能强大且灵活的持久层框架,它与传统的ORM框架相比,更加贴近SQL,提供了更细粒度的控制和优化数据库访问的能力。它的简洁性、可扩展性和高性能使其成为Java开发中常用的数据库访问框架之一。
MyBatis作为一个持久层框架,具有以下优点和缺点:
优点:
缺点:
总体而言,MyBatis是一个功能强大、灵活性高的持久层框架,它适用于需要对数据库操作进行精细控制和优化的项目。然而,它也需要开发人员对SQL和数据库操作有一定的了解,对于简单的CRUD操作,可能使用ORM框架更为便捷。
综上所述,#{}
是更安全和可靠的参数注入方式,能够有效防止SQL注入攻击,并进行参数值的类型转换。建议在编写MyBatis的SQL语句时,优先使用#{}
来处理参数,除非有特殊需求需要使用${}
进行字符串替换。
通过sql的limit 子句
分页插件的原理:拦截查询自己,修改成分页的形式,然后再执行
PageHelper
基于RowBounds
的分页方式
RowBounds rowBounds = new RowBounds(offset, limit);
List<User> userList = sqlSession.selectList("getUserList", null, rowBounds);
基于SELECT
语句参数的分页方式
<select id="getUserList" parameterType="map" resultMap="userResultMap">
SELECT * FROM user
LIMIT #{offset}, #{limit}
</select>
分页插件的方式
// 设置分页参数
PageHelper.startPage(pageNum, pageSize);
// 执行查询
List<User> userList = userDao.getUserList();
// 获取分页信息
PageInfo<User> pageInfo = new PageInfo<>(userList);
OFFSET
和LIMIT
来限制返回的数据量,适用于数据量较小的情况。支持,动态代理
https://juejin.cn/post/6844904062362583054
https://blog.csdn.net/friggly/article/details/124686876
MyBatis的延迟加载通过代理模式来实现。在查询时,MyBatis会返回一个代理对象而不是完整的实体对象。当访问代理对象的延迟加载属性时,MyBatis会根据需要执行额外的查询来加载相关数据。
延迟加载的实现原理如下:
需要注意的是,延迟加载只适用于关联关系的属性,即存在一对一或一对多的关系。对于非关联关系的普通属性,延迟加载无效。
为了实现延迟加载,MyBatis提供了两种配置方式:
通过使用延迟加载,可以减少不必要的数据库查询,提高查询效率和性能,特别是在处理复杂关联关系和大量数据的情况下,具有重要的优化意义。
MyBatis框架提供了三种执行器(Executor)来执行SQL语句和映射语句: