车子的个人博客

正确的事情通常都是优雅的

JPA实体重复问题解决

问题

同一实体对象被多个EntityManager管理。

解决方式

1
2
3
4
5
6
7
8
9
// 方式1:merge
User user = entityManager.merge(user);

// 方式2:clear
entityManager.clear();

// 方式3:remove + persist
entityManager.remove(user);
entityManager.persist(user);

总结

正确管理实体生命周期可避免重复问题。

Spring全家桶笔记:核心概念与配置

Spring概述

Spring是一个轻量级的Java开发框架,旨在简化企业级应用开发。其核心是控制反转(IOC)面向切面编程(AOP)

IOC容器

什么是IOC

IOC(Inversion of Control)即控制反转,将对象创建和管理权交给Spring容器,由容器负责注入依赖。

容器实现

  • BeanFactory:基础容器,延迟加载
  • ApplicationContext:增强版容器,继承BeanFactory

Bean配置方式

1. XML配置

1
2
3
4
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDao"/>
<property name="name" value="张三"/>
</bean>

2. 注解配置

1
2
3
4
5
6
7
8
9
@Component
public class UserService {

@Autowired
private UserDao userDao;

@Value("${app.name}")
private String appName;
}

3. Java配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public class AppConfig {

@Bean
public UserService userService() {
UserService service = new UserService();
service.setUserDao(userDao());
return service;
}

@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
}

Bean作用域

作用域 说明
singleton 单例(默认)
prototype 多例
request HTTP请求
session HTTP会话
application ServletContext生命周期

生命周期

  1. 实例化 → 2. 属性赋值 → 3. 初始化 → 4. 销毁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class UserService implements InitializingBean, DisposableBean {

@PostConstruct
public void init() {
System.out.println("初始化");
}

@PreDestroy
public void destroy() {
System.out.println("销毁");
}

@Override
public void afterPropertiesSet() {}

@Override
public void destroy() {}
}

依赖注入

注入方式

  • 构造器注入:推荐,清晰表示依赖关系
  • Setter注入:可选注入
  • 字段注入:简洁但不利于测试
1
2
3
4
5
6
7
8
9
10
11
12
@Service
public class OrderService {

private final UserService userService; // 构造器注入

@Autowired
private OrderDao orderDao; // 字段注入

public void setPaymentService(PaymentService paymentService) {
// Setter注入
}
}

自动装配

1
2
3
4
5
6
7
8
9
10
// byType:按类型自动装配
@Autowired
private UserDao userDao;

// byName:按名称自动装配
@Autowired
private UserDao userDaoImpl;

// 构造函数自动装配
public UserService(UserDao userDao) {}

AOP面向切面编程

概念

  • Aspect:切面
  • Join Point:连接点
  • Pointcut:切点
  • Advice:通知(增强)
  • Weaving:织入

通知类型

类型 说明
@Before 前置通知
@AfterReturning 后置通知
@AfterThrowing 异常通知
@After 最终通知
@Around 环绕通知

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Aspect
@Component
public class LoggingAspect {

@Pointcut("execution(* com.example.service.*.*(..))")
public void servicePointcut() {}

@Before("servicePointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("方法: " + joinPoint.getSignature());
}

@AfterReturning(pointcut = "servicePointcut()", returning = "result")
public void afterReturning(Object result) {
System.out.println("返回: " + result);
}

@Around("servicePointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end - start) + "ms");
return result;
}
}

常用配置

属性配置

1
2
3
4
5
6
7
8
9
10
11
12
13
# application.yml
spring:
application:
name: myapp
profiles:
active: dev

server:
port: 8080

custom:
name: test
max: 100
1
2
3
4
5
@Value("${custom.name}")
private String name;

@Value("${custom.max}")
private Integer max;

多环境配置

  • application.yml(默认)
  • application-dev.yml
  • application-prod.yml

条件配置

1
2
3
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
@ConditionalOnMissingBean(UserService.class)
@ConditionalOnClass(MongoTemplate.class)

Spring Boot自动配置

原理

@SpringBootApplication = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan

@EnableAutoConfiguration 通过spring.factories和META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports加载配置。

自定义-starter

  1. 创建autoconfigure模块
  2. 定义配置类
  3. 创建spring.factories
  4. 创建starter模块依赖autoconfigure

总结

Spring核心概念包括IOC容器、依赖注入和AOP。理解这些概念是掌握Spring全家桶的基础。Spring Boot通过自动配置简化了开发,需要理解其原理以便更好地定制和调试。

MySQL外键绑定问题

问题

外键约束无法创建或绑定失败。

常见原因

  1. 类型不匹配
  2. 字符集不一致
  3. 索引缺失

解决方案

1
2
3
4
5
6
7
8
-- 确保关联字段类型一致
ALTER TABLE child MODIFY parent_id BIGINT;

-- 确保字符集一致
ALTER TABLE child CONVERT TO CHARACTER SET utf8mb4;

-- 手动创建索引
CREATE INDEX idx_parent_id ON child(parent_id);

总结

外键使用需注意细节。

MySQL锁知识点总结

概述

数据库锁是数据库管理系统中用于控制并发访问的机制。在MySQL中,锁机制直接影响事务的隔离级别和并发性能。本文总结了MySQL中常见的锁知识点。

锁的分类

1. 按粒度划分

  • 表级锁:锁定整个表,开销小,并发度低
  • 行级锁:锁定单行记录,开销大,并发度高
  • 页级锁:锁定数据页,介于表级和行级之间

2. 按模式划分

  • 共享锁(S锁):允许事务读取数据,多个事务可以同时持有
  • 排他锁(X锁):允许事务修改数据,其他事务不能同时持有

3. 意向锁

  • 意向共享锁(IS):事务意图在某些行上设置共享锁
  • 意向排他锁(IX):事务意图在某些行上设置排他锁

InnoDB锁机制

行锁

InnoDB存储引擎支持行级锁,通过索引实现:

  • Record Lock:锁定索引记录
  • Gap Lock:锁定索引间隙,防止幻读
  • Next-Key Lock:Record Lock + Gap Lock的组合

锁等待与死锁

  • 锁等待:当事务请求的锁被其他事务持有时,需要等待
  • 死锁:两个或多个事务相互等待对方释放锁

锁与事务隔离级别

隔离级别 锁机制
READ UNCOMMITTED 无锁
READ COMMITTED 记录锁
REPEATABLE READ Next-Key Lock
SERIALIZABLE 间隙锁+表锁

常见问题

1. 死锁的预防

  • 尽量使用索引,避免全表扫描
  • 保持事务简短,减少持有锁的时间
  • 统一访问顺序

2. 锁监控

1
2
3
4
5
6
7
8
-- 查看当前事务
SELECT * FROM information_schema.INNODB_TRX;

-- 查看当前锁
SELECT * FROM information_schema.INNODB_LOCKS;

-- 查看锁等待
SELECT * FROM information_schema.INNODB_LOCK_WAITS;

总结

理解MySQL锁机制对于优化数据库性能和避免并发问题至关重要。在实际开发中,需要根据业务场景选择合适的锁策略,合理设计索引,避免长时间持有锁导致的性能问题。

Intellij IDEA使用技巧

快捷键

快捷键 说明
Ctrl+Shift+N 全局搜索
Ctrl+Shift+F 全局查找
Ctrl+Alt+L 格式化代码
Ctrl+D 复制当前行
Ctrl+Y 删除当前行
Ctrl+Alt+T 包裹代码
Alt+Enter 快速修复

常用功能

  • Live Templates:代码模板
  • File and Code Templates:文件模板
  • Postfix Completion:后缀补全

调试技巧

  • 条件断点
  • 异常断点
  • Evaluate Expression

总结

熟练使用IDE能显著提升开发效率。

Spring @Async异步执行详解

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class AsyncService {

@Async
public void asyncMethod() {
// 异步执行
}

@Async
public Future<String> asyncWithResult() {
return new AsyncResult<>("result");
}
}

启用异步

1
2
3
@EnableAsync
@Configuration
public class AsyncConfig {}

线程池

1
2
3
4
5
6
7
8
9
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
return executor;
}

总结

@Async简化了异步编程。

Java断言失效问题详解

什么是断言

断言是Java中的一种调试特性,用于验证程序假设。

1
assert x > 0 : "x must be positive";

断言默认关闭

1
2
3
4
5
# 启用断言
java -ea MyClass

# 禁用断言
java -da MyClass

常见问题

1. 线上环境断言不生效

1
2
// 代码中写了断言,但线上默认关闭
assert validate(input);

2. 断言会影响性能

1
2
3
4
// 复杂验证应使用条件判断
if (!validate(input)) {
throw new IllegalArgumentException();
}

最佳实践

  • 断言用于开发阶段验证
  • 重要校验不要依赖断言
  • 生产环境应使用if-else校验

总结

断言是调试工具,不是校验工具。重要逻辑应使用异常处理。

Mac使用技巧整理

常用快捷键

  • Command+C/V:复制/粘贴
  • Command+Z:撤销
  • Command+Space:Spotlight搜索
  • Command+Tab:切换应用
  • Command+Shift+3/4:截图

终端命令

1
2
3
open .           # 打开当前目录
pbcopy < file # 复制文件内容
pbpaste # 粘贴

软件推荐

  • Homebrew:包管理器
  • iTerm2:终端
  • Alfred:效率启动器

总结

Mac是开发者的好帮手。

Spring @Schedule定时任务详解

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class ScheduledTask {

// 每5秒执行
@Scheduled(fixedRate = 5000)
public void task1() {}

// 每分钟执行
@Scheduled(cron = "0 * * * * *")
public void task2() {}

// 延迟3秒后执行
@Scheduled(fixedDelay = 3000)
public void task3() {}
}

常用配置

  • fixedRate:固定频率
  • fixedDelay:固定延迟
  • cron:Cron表达式
  • initialDelay:初始延迟

启用定时任务

1
2
3
@EnableScheduling
@SpringBootApplication
public class Application {}

总结

@Schedule是简单的定时任务方案。

Java8时间格式化详解

新时间API

Java 8引入了java.time包,提供了全新日期时间API。

LocalDateTime

1
2
3
4
5
6
7
8
9
LocalDateTime now = LocalDateTime.now();
LocalDateTime of = LocalDateTime.of(2024, 1, 1, 12, 0);

// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str = now.format(formatter);

// 解析
LocalDateTime parse = LocalDateTime.parse("2024-01-01 12:00:00", formatter);

DateTimeFormatter

1
2
3
4
5
// 常用格式
DateTimeFormatter.ofPattern("yyyy-MM-dd");
DateTimeFormatter.ofPattern("HH:mm:ss");
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter.ofPattern("yyyy年MM月dd日");

时间计算

1
2
LocalDateTime plus = now.plusDays(1).plusHours(2);
LocalDateTime minus = now.minusMonths(1);

与旧API互转

1
2
3
4
5
6
7
8
9
// Date -> LocalDateTime
Date date = new Date();
Instant instant = date.toInstant();
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());

// LocalDateTime -> Date
LocalDateTime ldt = LocalDateTime.now();
Instant instant = ldt.atZone(ZoneId.systemDefault()).toInstant();
Date date = Date.from(instant);

总结

Java 8时间API更加清晰易用,建议替换掉旧的Date和Calendar。

0%