数据库
声明式事务
原理,依然是自动创建了 Bean 的 Proxy:
public class UserService$$EnhancerBySpringCGLIB extends UserService {
UserService target = ...
PlatformTransactionManager txManager = ...
public User register(String email, String password, String name) {
TransactionStatus tx = null;
try {
tx = txManager.getTransaction(new DefaultTransactionDefinition());
target.register(email, password, name);
txManager.commit(tx);
} catch (RuntimeException e) {
txManager.rollback(tx);
throw e;
}
}
...
}
事务的传播与合并:
- 利用了 ThreadLocal,来判断当前线程是否已有事务。
常见的事务传播级别(propogation):
- 默认 REQUIRED
- SUPPORTS
:表示如果有事务,就加入到当前事务,如果没有,那也不开启事务执行。这种传播级别可用于查询方法,因为SELECT语句既可以在事务内执行,也可以不需要事务;
- MANDATORY
:表示必须要存在当前事务并加入执行,否则将抛出异常。这种传播级别可用于核心更新逻辑,比如用户余额变更,它总是被其他事务方法调用,不能直接由非事务方法调用;
使用 DAO
Data Access Object,就是一个简单的数据访问模式。
public abstract class AbstractDao<T> extends JdbcDaoSupport {
private String table;
private Class<T> entityClass;
private RowMapper<T> rowMapper;
public AbstractDao() {
// 获取当前类型的泛型类型:
this.entityClass = getParameterizedType();
this.table = this.entityClass.getSimpleName().toLowerCase() + "s";
this.rowMapper = new BeanPropertyRowMapper<>(entityClass);
}
public T getById(long id) {
return getJdbcTemplate().queryForObject("SELECT * FROM " + table + " WHERE id = ?", this.rowMapper, id);
}
public List<T> getAll(int pageIndex) {
int limit = 100;
int offset = limit * (pageIndex - 1);
return getJdbcTemplate().query("SELECT * FROM " + table + " LIMIT ? OFFSET ?",
new Object[] { limit, offset },
this.rowMapper);
}
public void deleteById(long id) {
getJdbcTemplate().update("DELETE FROM " + table + " WHERE id = ?", id);
}
...
}
这样,每个子类就自动获得了这些通用方法:
@Component
@Transactional
public class UserDao extends AbstractDao<User> {
// 已经有了:
// User getById(long)
// List<User> getAll(int)
// void deleteById(long)
}
@Component
@Transactional
public class BookDao extends AbstractDao<Book> {
// 已经有了:
// Book getById(long)
// List<Book> getAll(int)
// void deleteById(long)
}