同时插入商品对应的使用时间数据,需要操作两张表:product,product_usetime。在productService接口中定义save方法,该方法接受一张Dto对象,dto对象继承自product类,并将product_usetime的List集合作为成员属性。通过productService实现类的save方法,将dto对象保存至数据库。然后取出dto实体中的商品id,遍历Dto的元素类型为product_usetime的List集合,并将商品id赋给product_usetime对象,最后将product_usetime的集合存入数据库。
代码中使用了 @Transactional
注解,表示该方法应该在事务中执行,即保证整个方法的原子性,如果方法执行过程中出现异常,则会回滚已执行的操作。
大致思路:构造分页对象,添加查询添加,例如根据name模糊查询还有排序条件,使用商品业务实现类的page方法进行分页查询,得到pageInfo对象,因为pageInfo的records字段存储的商品类中只有分类id,没有分类名称,因此用dto对象来替代records字段存储的商品类,dto类继承自商品类,并且将分类名称作为它的成员属性。
操作多张表,在service接口中扩展方法。
业务逻辑如下:
通过ThreadLocal获得用户id,根据用户id获得用户数据和购物车数据,根据客户端传来的收货地址查询地址数据,使用IdWorker.getId() 方法创建一个订单号。
对购物车数据创建一个Stream流,使用map方法对流中的每个元素进行映射操作,设置订单明细对象的值,并计算订单的总额,最终向订单明细表插入数据。
设置订单对象的值,其订单金额由上一步计算得来,最终向订单表插入数据。
最后删除购物车数据。
需求:根据商品表获取商品使用时间表。
ThreadPoolExecutor
的单例线程池, 用于管理线程的执行。SynchroniseUtil
对象,参数为子任务的数量,用于同步子任务的执行。orders
。OrderTask
对象,该对象封装了子列表 orderVOSubList
、用户列表 users
和同步器 synchroniseUtil
。execute
方法提交 OrderTask
,使其在线程池中执行。Task
类实现了 Runnable
接口,并重写了 run()
方法。run()
方法是在子线程中执行的代码逻辑。其目的是获取对应商品的使用时间数据,并将使用时间数据添加到dto对象,该dto对象继承自商品对象,并且包含使用时间的字段。synchroniseUtil
中。synchroniseUtil
,每个线程调用 addResult()
方法后都能将计数器的值减1,当所有线程都完成时,调用 get()
方法获取结果。为常作为查询条件的字段建立索引,where子句中的列,或者连接子句中指定的列
为经常需要排序、分组操作的字段建立索引
更新频繁字段不适合创建索引
不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)
对于定义为text、image和bit的数据类型的列不要建立索引,使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间
使用覆盖索引:创建覆盖索引,即索引包含查询所需的全部列,可以减少查询的IO操作,提高查询性能。
最左前缀原则,就是最左边的优先。指的是联合索引中,优先走最左边列的索引。对于多个字段的联合索引,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
非空字段:应该指定列为NOT NULL,除非你想存储NULL。
不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长
拦截器和过滤器的区别:
过滤器能够对所有请求进行过滤处理,而拦截器通常只针对特定的控制器或控制器方法进行拦截。
执行时机:
使用范围:
所属技术框架:
过滤器的实现:
通过@WebFilter
注解将该类声明为一个过滤器,并指定过滤器应用于所有的URL路径("/*")。
实现Filter
接口,并实现其中的doFilter
方法,获取HttpServletRequest
和HttpServletResponse
对象。通过request.getRequestURI()
获取当前请求的URI路径。
定义一个包含不需要进行登录检查的请求路径数组urls,
调用check
方法,将urls
数组和当前请求的URI进行匹配,判断本次请求是否需要进行登录检查。
如果不需要进行登录检查,则直接调用filterChain.doFilter(request, response)
放行请求,将请求传递给目标资源。
如果需要进行登录检查,则判断是否登录,如果已登录,则将用户ID存入ThreadLocal
对象中,然后调用filterChain.doFilter(request, response)
放行请求。
如果未登录,则通过输出流的方式向客户端响应一个包含错误信息的JSON字符串,表示未登录状态。
在全局异常处理器中,使用@ControllerAdvice注解标注该类,并通过annotations属性指定要增强的控制器类型为@RestController和@Controller,即RESTful风格的控制器和普通控制器。
使用@ResponseBody注解将方法的返回值直接作为响应体返回,而不需要额外的视图解析。
定义了一个异常处理方法 exceptionHandler
,用于处理 SQL完整性约束冲突
类型的异常。当捕获到该异常时,会执行该方法进行处理。
处理逻辑如下:
简要解释动态代理:动态代理是一种在运行时创建代理对象的技术,通过代理对象可以在不修改原始对象的情况下拦截并增强方法的调用。
提及动态代理的两种类型:
基于接口的动态代理:基于接口的动态代理是通过Java的反射机制在运行时动态创建代理对象。该代理对象实现了目标接口,并将方法调用委托给实际的对象。我会提到java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口在基于接口的动态代理中的重要性。
基于类的动态代理:我会解释基于类的动态代理是通过继承和字节码操作在运行时动态创建代理对象。我会提到java.lang.reflect.Proxy
类和java.lang.reflect.MethodInterceptor
接口在基于类的动态代理中的作用。
动态代理的应用场景:我会提及动态代理的一些常见应用场景,如日志记录、事务管理、权限控制等。
是一种创建型设计模式,用于确保一个类只有一个实例,因此类的构造方法为private并且拥有一个当前类的静态成员变量,并提供对该实例的全局访问点,向外界再提供一个静态方法,向外界提供当前类的实例,当前类只能在内部实例化。序列号生成器、web页面计数器或者创建对象需要消耗较多资源时,可用单例模式。单例模式的最佳实践为无状态的,比如以工具类的形式提供。
项目实践:使用单例模式管理数据库连接对象,避免频繁地创建和销毁连接,提高性能和资源利用率。
提供了一个代理对象作为另一个对象的替代,以控制对该对象的访问。代理对象充当原始对象的接口,可以提供额外的功能,如延迟初始化、访问控制或缓存。例如在分布式系统中,客户端通过代理对象访问远程服务,代理对象负责网络通信和数据传输。
项目实践:过滤器模式是代理模式的一种特殊情况,它通过代理对象对目标对象进行包装,以在目标对象的方法执行前后添加额外的处理逻辑。
定义了对象之间的一对多依赖关系。当一个对象(主题)的状态发生变化时,所有依赖对象(观察者)都会被通知并自动更新。这种模式实现了松耦合的通信,使对象能够对变化做出反应而无需紧密耦合在一起。例如在数据库系统中,触发器可以通过观察者模式实现,当数据库中的数据发生变化时触发相应的操作。
用于使具有不兼容接口的对象能够共同工作。它提供了一个适配器作为两个不兼容对象之间的桥梁,将它们的接口进行转换,以使它们能够协同工作。在不同的数据格式之间进行转换时,适配器模式可以提供一个统一的接口来适应不同的数据格式。
项目实践:对象映射器(Object Mapper)用于将Java对象与JSON之间进行转换。
1、实例化bean:反射的方式生成对象。
2、填充bean的属性: 使用populateBean方法将属性值填充到一个 Java 对象,在这里会使用三级缓存来解决循环依赖的问题 。
3、调用aware接口相关的方法: 调用invokeAwareMethods
方法,完成对BeanName,BeanFactory,BeanClassLoader对象的属性设置。
4、调用BeanPostProcessor中的前置处理方法: 使用比较多的有 ApplicationContextPostProcessor,设置ApplicationContext,Environment等对象,可以在应用上下文初始化的早期对Spring容器进行自定义的配置和扩展,满足应用程序特定的需求。
5、调用initmethod方法:会首先判断是否实现了InitializingBean接口。如果实现了该接口,Spring会自动调用其afterPropertiesSet方法,以执行Bean的初始化逻辑。如果没有实现InitializingBean接口,Spring则不会调用afterPropertiesSet方法。
6、用BeanPostProcessor的后置处理方法: spring的aop就是在此处实现的。
7、获取到完整的对象,可以通过getBean的方式来进行对象的获取
8、在销毁流程中,首先会判断Bean是否实现了DisposableBean接口。如果实现了该接口,Spring会自动调用其destroy方法,以执行Bean的销毁逻辑。如果没有实现DisposableBean接口,Spring则会查看配置中是否指定了destroyMethod方法名,并在销毁时调用该方法。