().where().eq("user_id", user.getUserId()).build();
System.err.println("该条目:" + userBaseDao.select(sqlDecorator));
/**
* 测试更新
*/
user.setLoginName("????sdjks");
System.err.println("更新结果:" + userBaseDao.update(user, sqlDecorator));
System.err.println("被更新条目:" + userBaseDao.select(sqlDecorator));
/**
* 测试删除
*/
System.err.println("删除结果:" + userBaseDao.delete(sqlDecorator));
System.err.println("该条目:" + userBaseDao.select(sqlDecorator));
/**
* 测试查询条目
*/
System.err.println("总数据量:" + userBaseDao.count(null));
}
}
```
### 多表联查
#### 方案一,从表实体对象作为主表的字段
主表:tb_user
```java
@Getter
@Setter
@Entity("tb_user")
public class User implements Serializable {
@PrimaryKey(strategyKey = KeyStrategyManager.JDBC_AUTO, convertor = IntegerConvertor.class)
private int userId;
private String loginName;
private String password;
private int roleId;
@Join(slaveTable = Role.class, masterTableField = "roleId", slaveTableField = "roleId", joinType = JoinType.LEFT, isManyToMany = false)
private Role role;
}
```
从表:tb_role
```java
@Getter
@Setter
@Entity("tb_role")
public class Role {
@PrimaryKey(strategyKey = KeyStrategyManager.JDBC_AUTO, convertor = IntegerConvertor.class)
private int roleId;
private String roleName;
}
```
#### 方案二,从表的字段作为主表的字段存在于主表实体类中
主表:tb_user
```java
@Getter
@Setter
@Entity("tb_user")
@JoinTable(slaveTableName = "tb_role", masterTableField = "roleId", slaveTableJoinColumn = "role_id", joinType = JoinType.LEFT, isManyToMany = false)
public class User implements Serializable {
@PrimaryKey(strategyKey = KeyStrategyManager.JDBC_AUTO, convertor = IntegerConvertor.class)
private int userId;
private String loginName;
private String password;
private int roleId;
@SlaveField(slaveTableName = "tb_role")//标记为从表字段
private String roleName;
}
```
### 多数据源实时切换
首先,在yaml文件添加多数据源的配置:
application.yaml
```yaml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
first:
url: jdbc:mysql://localhost:9999/velox-sql-demo-1?useUnicode=true&characterEncoding=UTF8
driver-class-name: com.mysql.jdbc.Driver
username: # 你的用户名
password: # 你的密码
second:
url: jdbc:mysql://localhost:9999/velox-sql-demo-2?useUnicode=true&characterEncoding=UTF8
driver-class-name: com.mysql.jdbc.Driver
username: # 你的用户名
password: # 你的密码
```
VeloxSqlConfig.java
```java
@Configuration
public class VeloxSqlConfig extends VeloxAdvancedConfiguration {
public VeloxSqlConfig() {
}
@Resource
@Override
public void setInjectEnhanceBean(InjectEnhanceBean injectEnhanceBean) {
super.setInjectEnhanceBean(injectEnhanceBean);
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.first")
public DataSource dataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.second")
public DataSource secondDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Override
protected void configure(com.maxwellnie.velox.sql.core.config.Configuration configuration, InjectEnhanceBean injectEnhanceBean, DataSource defaultDataSource, TransactionFactory transactionFactory) {
configuration.setCache(true, LRUCache.class);
configuration.setLevel(0);
configuration.setStandTable(true);
configuration.setStandColumn(true);
configuration.setTablePrefix("tb_");
configuration.setDialect(new MySqlDialect());
configuration.setTaskQueue(new DefaultTaskQueue());
DataSourceManager.register("first",dataSource());
DataSourceManager.register("second",secondDataSource());
}
}
```
对于多数据源,提供了两种方式来管理多数据源的事务。
#### 第一种方式,用户自行管理Session
在使用spring框架时通过SessionTransactionManager来管理Session,并且实现com.maxwellnie.velox.sql.core.distributed.TransactionTask接口来实现多数据源情况下事务的提交逻辑
实现事务任务
```java
/**
* 代理事务真正的实现,请注意,本代理事务在多数据源情境下非链式事务,而是直接对所有待处理事务进行操作,并不关注其中某个事务成功与否
* 多数据源情境下不建议使用!!!!!!!!!
* @author Maxwell Nie
*/
public class NoSpringTransactionTask implements TransactionTask {
private static final Logger logger = LoggerFactory.getLogger(NoSpringTransactionTask.class);
/**
* 待处理事务的元数据
*/
private final List metaDataList = new LinkedList<>();
/**
* 添加待处理事务元数据
* @param metaData
*/
@Override
public void add(MetaData metaData) {
metaDataList.add(metaData);
}
/**
* 回滚事务
* @return
*/
@Override
public boolean rollback() {
logger.debug("Transaction task start rollback.");
int i = 0;
for (MetaData metaData : metaDataList) {
Connection connection = metaData.getProperty("connection");
// 这里判断两种情况,一是当前框架事务被用户所代理(或者被框架提供的事务管理器所管理),二是当前框架事务被用户提供的spring事务管理器所管理
if (connection != null && (CurrentSession.isOpenProxyTransaction() || DataSourceUtils.isConnectionTransactional(connection, metaData.getProperty("dataSource")))) {
try {
logger.debug("rollback " + i++);
connection.rollback();
} catch (Exception e) {
throw new JDBCConnectionException(e);
}
}
}
return true;
}
/**
* 提交事务
* @return
*/
@Override
public boolean commit() {
logger.debug("Transaction task start committing.");
int i = 0;
for (MetaData metaData : metaDataList) {
Connection connection = metaData.getProperty("connection");
if (connection != null && (CurrentSession.isOpenProxyTransaction() || DataSourceUtils.isConnectionTransactional(connection, metaData.getProperty("dataSource")))) {
try {
logger.debug("commit " + i++);
connection.commit();
} catch (Exception e) {
throw new JDBCConnectionException(e);
}
}
}
return true;
}
/**
* 关闭事务
*/
@Override
public void close() {
logger.debug("Transaction task start closing.");
for (MetaData metaData : metaDataList) {
Connection connection = metaData.getProperty("connection");
if (connection != null) {
try {
if (!CurrentSession.isOpenProxyTransaction())
DataSourceUtils.releaseConnection(connection, metaData.getProperty("dataSource"));
else
connection.close();
} catch (Exception e) {
throw new JDBCConnectionException(e);
}
}
}
}
}
```
在配置文件中指定事务任务的实现
```yaml
velox-sql:
global:
transaction-task-class: com.maxwellnie.velox.sql.spring.support.NoSpringTransactionTask
```
#### 第二种方式,使用SessionTransactionManager(推荐)
在配置类中添加下列代码
```java
@Bean
public PlatformTransactionManager transactionManager(SessionFactory sessionFactory){
return new SessionTransactionManager(sessionFactory);
}
```
最后在业务层使用CurrentThreadUtils切换数据源,切换的键是存入DataSourceManager中的键。
```java
@Transactional
public List move(User user0,User user1,SqlDecorator sqlDecorator){
CurrentSession.setDataSourceName("second");
int count=userDao.insert(user1);
System.out.println(userDao.select(null).size());
if(count<=0)
throw new RuntimeException();
CurrentSession.clearDataSourceName();
int count1=userDao.update(user1,sqlDecorator);
if(count1<=0)
throw new RuntimeException();
return userDao.select(null);
}
```
### 对框架的功能进行增强或是扩展
BaseDao是为开发者预留的操作数据库的接口,提供了大量方法操作数据库。
这些方法的实现由方法执行器提供,方法执行器共有8个阶段,检查、预处理、SQL生成、Statement创建、SQL执行、结果处理、结果缓存、关闭Statement。
我们可以实现AbstractMethodHandler抽象类,对方法执行器的阶段进行拦截,并且设置该拦截器的序号,序号越大,拦截器的优先级越高,越先运行。最大不要超过9999。
在此例子中,我们拦截了BaseDao接口的count方法,我们拦截了方法执行流程的openStatement方法,将limit条件置空。并且拦截了其他他方法,实现了查询条目功能。
```java
public class CountMethodHandler extends AbstractMethodHandler {
//设定拦截器序号、被拦截的环节、被增强的BaseDao方法。
public CountMethodHandler() {
super(999, new MethodAspect[]{
new MethodAspect("buildRowSql", new Class[]{
MetaData.class
}),
new MethodAspect("openStatement", new Class[]{
RowSql.class,
Session.class,
TableInfo.class,
Object[].class
})
}, new TargetMethodSignature("count", new Class[]{SqlDecorator.class}));
}
@Override
public Object handle(SimpleInvocation simpleInvocation) {
if (simpleInvocation.getArgs().length == 1) {
MetaData metaData = (MetaData) simpleInvocation.getArgs()[0];
SqlDecorator> sqlDecorator = metaData.getProperty("sqlDecorator");
if (sqlDecorator != null) {
sqlDecorator.setLimitFragment(null);
}
try {
return simpleInvocation.targetMethod.invoke(simpleInvocation.getTarget(), metaData);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new ExecutorException(e);
}
} else {
RowSql rowSql = (RowSql) simpleInvocation.getArgs()[0];
String sql = rowSql.getNativeSql();
TableInfo tableInfo = (TableInfo) simpleInvocation.getArgs()[2];
int fromIndex = sql.indexOf("FROM");
sql = sql.substring(fromIndex);
String count = "COUNT(*)";
if (tableInfo.hasPk()) {
count = "COUNT(" + tableInfo.getTableName() + "." + tableInfo.getPkColumn().getColumnName() + ")";
}
sql = "SELECT" + SqlPool.SPACE + count + SqlPool.SPACE + sql;
rowSql.setNativeSql(sql);
try {
return simpleInvocation.targetMethod.invoke(simpleInvocation.getTarget(), simpleInvocation.getArgs());
} catch (IllegalAccessException | InvocationTargetException e) {
throw new ExecutorException(e);
}
}
}
}
```
当然,上述方法并不方便,我们为你提供了更加便捷的方式,我们可以通过继承EnhancedMethodHandler类,选择代理的方法,将其复制到子类中,并且将参数列表的第一个位置增加SimpleInvocation参数。
```java
/**
* @author Maxwell Nie
*/
@MethodHandlerProxy
public class CountMethodHandler extends EnhancedMethodHandler {
public CountMethodHandler() {
super(999, new TargetMethodSignature("count", new Class[]{SqlDecorator.class}));
}
@MethodInterceptor
public RowSql buildRowSql(SimpleInvocation simpleInvocation, MetaData metaData) throws ExecutorException{
SqlDecorator> sqlDecorator = metaData.getProperty("sqlDecorator");
if (sqlDecorator != null) {
sqlDecorator.setLimitFragment(null);
}
try {
return (RowSql) simpleInvocation.targetMethod.invoke(simpleInvocation.getTarget(), metaData);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new ExecutorException(e);
}
}
@MethodInterceptor
public StatementWrapper openStatement(SimpleInvocation simpleInvocation, RowSql rowSql, Session session, TableInfo tableInfo, Object[] args) throws ExecutorException{
String sql = rowSql.getNativeSql();
int fromIndex = sql.indexOf("FROM");
sql = sql.substring(fromIndex);
String count = "COUNT(*)";
if (tableInfo.hasPk()) {
count = "COUNT(" + tableInfo.getTableName() + "." + tableInfo.getPkColumn().getColumnName() + ")";
}
sql = "SELECT" + SqlPool.SPACE + count + SqlPool.SPACE + sql;
rowSql.setNativeSql(sql);
try {
return (StatementWrapper) simpleInvocation.targetMethod.invoke(simpleInvocation.getTarget(), simpleInvocation.getArgs());
} catch (IllegalAccessException | InvocationTargetException e) {
throw new ExecutorException(e);
}
}
}
```
最后,我们需要将拦截器添加到框架中,如下,在VeloxSqlConfig类的configure方法中添加如下代码。InjectEnhanceBean可以监听Context的构建,并且在Context构建后将拦截器注入到框架中。
```java
public void configure(com.maxwellnie.velox.sql.core.config.Configuration configuration, InjectEnhanceBean injectEnhanceBean, DataSource defaultDataSource, TransactionFactory transactionFactory) {
injectEnhanceBean.addMethodHandler(new CountMethodHandler());
}
```
### 适配其他数据库
首先,我们需要实现Dialect接口的getDialectRowSql方法:
```java
public interface Dialect {
RowSql getDialectRowSql(RowSql rowSql, long start, long offset);
}
```
RowSql本质是一条SQL语句,所以我们可以对其修改以适配其他数据库,下面是一个示例,适配mysql数据库:
```java
public class MySqlDialect implements Dialect {
@Override
public RowSql getDialectRowSql(RowSql rowSql, long start, long offset) {
String sql = rowSql.getNativeSql();
sql = sql + SqlPool.SPACE + "LIMIT " + start + ", " + offset;
rowSql.setNativeSql(sql);
return rowSql;
}
}
```
接着,在配置类指定方言:
```java
@Override
protected void configure(com.maxwellnie.velox.sql.core.config.Configuration configuration, InjectEnhanceBean injectEnhanceBean, DataSource defaultDataSource, TransactionFactory transactionFactory) {
configuration.setDialect(new MySqlDialect());
}
```
### 适配新的类型
我们可以通过实现Convertor接口,将自定义类型转换为数据库类型:
```java
public class BigDecimalConvertor implements TypeConvertor {
@Override
public BigDecimal convert(ResultSet resultSet, String column) throws SQLException {
BigDecimal value = resultSet.getBigDecimal(column);
if (value == null)
return new BigDecimal("0");
else
return value;
}
//数据库数据转换到Java类型
@Override
public BigDecimal convert(ResultSet resultSet, int columnIndex) throws SQLException {
BigDecimal value = resultSet.getBigDecimal(columnIndex);
if (value == null)
return new BigDecimal("0");
else
return value;
}
//Java对象设置到数据库
@Override
public void addParam(PreparedStatement preparedStatement, int index, Object param) throws SQLException {
if (param == null)
preparedStatement.setBigDecimal(index, null);
else if (param instanceof String)
preparedStatement.setBigDecimal(index, new BigDecimal((String) param));
else if (param instanceof Number)
preparedStatement.setBigDecimal(index, new BigDecimal(param.toString()));
else
throw new TypeConvertException("The param [" + param + "] is not convert to java.math.BigDecimal");
}
//空值
@Override
public BigDecimal getEmpty() {
return new BigDecimal("0");
}
}
```
然后在需要这个类型转换器的列指定类型转换器:
```java
@Getter
@Setter
@Entity("tb_user")
@JoinTable(slaveTableName = "tb_role", masterTableField = "roleId", slaveTableColumn = "role_id", joinType = JoinType.LEFT, isManyToMany = false)
public class User implements Serializable {
//指定类型转换器
@PrimaryKey(strategyKey = KeyStrategyManager.JDBC_AUTO, convertor = IntegerConvertor.class)
private int userId;
private String loginName;
private String password;
private int roleId;
@SlaveField(slaveTableName = "tb_role")//标记为从表字段
private String roleName;
}
```
### 支持Json
需要实现JsonSupport接口,并且设置到高级设置中:
```java
public class JsonSupporterImpl implements JsonSupporter {
.....
}
```
VeloxSqlConfig中添加如下代码:
```java
@Override
protected void configure(com.maxwellnie.velox.sql.core.config.Configuration configuration, InjectEnhanceBean injectEnhanceBean, DataSource defaultDataSource, TransactionFactory transactionFactory) {
configuration.setJsonSupporter(new JsonSupportImpl());
}
```
# 致谢
这是我大学毕设答辩的题目,并且它获得了一个很高的分数,我希望这个项目能逐渐成长为更适合主流网站的ORM框架,如果你愿意为此项目做贡献,我将不胜感激。
# 联系我们
邮箱:maxwellnie@qq.com