This commit is contained in:
Redkale
2016-09-07 16:19:14 +08:00
parent d202b2fbad
commit fbb9cdefe1
5 changed files with 174 additions and 19 deletions

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 其配置算是标准的JPA配置文件的缩略版 -->
<persistence>
<!-- 系统基本库 -->
<persistence-unit name="demouser">
<!-- 为NONE表示不启动缓存@Cacheable 失效; 非NONE值(通常用ALL)表示开启缓存。 -->
<shared-cache-mode>NONE</shared-cache-mode>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbuser?characterEncoding=utf8"/>
<!--
javax.persistence.jdbc.driver在JPA的值是JDBC驱动Redkale有所不同值应该是javax.sql.DataSource的子类。
为了兼容用户习惯Redkale内置常见JDBC驱动到javax.sql.DataSource的映射关系
com.mysql.jdbc.Driver —————— com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
org.mariadb.jdbc.Driver —————— org.mariadb.jdbc.MySQLDataSource
oracle.jdbc.driver.OracleDriver —————— oracle.jdbc.pool.OracleConnectionPoolDataSource
com.microsoft.sqlserver.jdbc.SQLServerDriver —————— com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource
因此 com.mysql.jdbc.Driver 会被自动转换成 com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
-->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="123456"/>
<!-- 最大连接数默认值CPU数*16 -->
<property name="javax.persistence.connections.limit" value="32"/>
<!-- 包含的SQL模板相当于反向LIKE不同的JDBC驱动的SQL语句不一样Redkale内置了MySQL、Oracle、Sqlserver的语句 -->
<property name="javax.persistence.contain.sqltemplate" value="LOCATE(${keystr}, ${column}) > 0"/>
<property name="javax.persistence.notcontain.sqltemplate" value="LOCATE(${keystr}, ${column}) = 0"/>
<!-- 复制表结构的SQL模板Redkale内置了MySQL的语句 -->
<property name="javax.persistence.tablenotexist.sqlstate" value="42S02"/>
<property name="javax.persistence.tablecopy.sqltemplate" value="CREATE TABLE ${newtable} LIKE ${oldtable}"/>
</properties>
</persistence-unit>
<!-- IM消息库 -->
<persistence-unit name="demoim">
<shared-cache-mode>NONE</shared-cache-mode>
<properties>
<!-- jdbc:mysql://127.0.0.1:3306/dbim?autoReconnect=true&amp;autoReconnectForPools=true&amp;characterEncoding=utf8 -->
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbim?characterEncoding=utf8"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="123456"/>
</properties>
</persistence-unit>
</persistence>

View File

@@ -36,6 +36,10 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
static final String JDBC_NOTCONTAIN_SQLTEMPLATE = "javax.persistence.notcontain.sqltemplate";
static final String JDBC_TABLENOTEXIST_SQLSTATE = "javax.persistence.tablenotexist.sqlstate";
static final String JDBC_TABLECOPY_SQLTEMPLATE = "javax.persistence.tablecopy.sqltemplate";
static final String JDBC_URL = "javax.persistence.jdbc.url";
static final String JDBC_USER = "javax.persistence.jdbc.user";
@@ -329,7 +333,7 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
if (values.length == 0) return;
try {
if (!info.isVirtualEntity()) {
final String sql = info.insertSQL;
final String sql = info.getInsertSQL(values[0]);
final PreparedStatement prestmt = info.autoGenerated
? conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) : conn.prepareStatement(sql);
final Class primaryType = info.getPrimary().type();
@@ -340,19 +344,24 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
if (distributed && !info.initedPrimaryValue && primaryType.isPrimitive()) { //由DataSource生成主键
synchronized (info) {
if (!info.initedPrimaryValue) { //初始化最大主键值
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT MAX(" + info.getPrimarySQLColumn() + ") FROM " + info.getTable(values[0]));
if (rs.next()) {
if (primaryType == int.class) {
int v = rs.getInt(1) / info.allocationSize;
if (v > info.primaryValue.get()) info.primaryValue.set(v);
} else {
long v = rs.getLong(1) / info.allocationSize;
if (v > info.primaryValue.get()) info.primaryValue.set(v);
try {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT MAX(" + info.getPrimarySQLColumn() + ") FROM " + info.getTable(values[0]));
if (rs.next()) {
if (primaryType == int.class) {
int v = rs.getInt(1) / info.allocationSize;
if (v > info.primaryValue.get()) info.primaryValue.set(v);
} else {
long v = rs.getLong(1) / info.allocationSize;
if (v > info.primaryValue.get()) info.primaryValue.set(v);
}
}
rs.close();
stmt.close();
} catch (SQLException se) {
if (info.tableStrategy == null) throw se;
}
rs.close();
stmt.close();
info.initedPrimaryValue = true;
}
}
@@ -393,7 +402,22 @@ public final class DataDefaultSource implements DataSource, Function<Class, Enti
sqls[index++] = sb.toString();
}
}
prestmt.executeBatch();
try {
prestmt.executeBatch();
} catch (SQLException se) {
if (info.tableStrategy == null || !info.tablenotexistSqlstate.equals(se.getSQLState())) throw se;
synchronized (info.tables) {
final String oldTable = info.table;
final String newTable = info.getTable(values[0]);
if (!info.tables.contains(newTable)) {
Statement st = conn.createStatement();
st.execute(info.tablecopySQL.replace("${newtable}", newTable).replace("${oldtable}", oldTable));
st.close();
info.tables.add(newTable);
}
}
prestmt.executeBatch();
}
if (writeListener != null) writeListener.insert(sqls);
if (info.autoGenerated) { //由数据库自动生成主键值
ResultSet set = prestmt.getGeneratedKeys();

View File

@@ -0,0 +1,24 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.source;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
*
* <p>
* 详情见: http://redkale.org
*
* @author zhangjx
*/
@Target({TYPE})
@Retention(RUNTIME)
public @interface DistributeTable {
Class<? extends DistributeTableStrategy> strategy();
}

View File

@@ -0,0 +1,29 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.source;
import java.io.Serializable;
/**
*
* <p>
* 详情见: http://redkale.org
*
* @author zhangjx
* @param <T>
*/
public interface DistributeTableStrategy<T> {
default String getTable(String table, Serializable primary) {
return null;
}
default String getTable(String table, FilterNode node) {
return null;
}
public String getTable(String table, T bean);
}

View File

@@ -37,7 +37,7 @@ public final class EntityInfo<T> {
private final Class<T> type;
//类对应的数据表名, 如果是VirtualEntity 类, 则该字段为null
private final String table;
final String table;
private final Creator<T> creator;
@@ -62,11 +62,19 @@ public final class EntityInfo<T> {
final String notcontainSQL; //用于反向LIKE使用
final String tablenotexistSqlstate; //用于判断表不存在的使用
final String tablecopySQL; //用于复制表结构使用
final Set<String> tables = new HashSet<>(); //用于存在table_20160202类似这种分布式表
final DistributeTableStrategy<T> tableStrategy;
final String querySQL;
private final Attribute<T, Serializable>[] queryAttributes; //数据库中所有字段
final String insertSQL;
private String insertSQL;
final Attribute<T, Serializable>[] insertAttributes; //数据库中所有可新增字段
@@ -145,6 +153,15 @@ public final class EntityInfo<T> {
this.fullloader = fullloader;
this.table = (t == null) ? type.getSimpleName().toLowerCase() : (t.catalog().isEmpty()) ? t.name() : (t.catalog() + '.' + t.name());
}
DistributeTable dt = type.getAnnotation(DistributeTable.class);
DistributeTableStrategy dts = null;
try {
dts = (dt == null) ? null : dt.strategy().newInstance();
} catch (Exception e) {
logger.severe(type + " init DistributeTableStrategy error", e);
}
this.tableStrategy = dts;
this.creator = Creator.create(type);
Attribute idAttr0 = null;
Map<String, String> aliasmap0 = null;
@@ -231,7 +248,7 @@ public final class EntityInfo<T> {
if (insertsb2.length() > 0) insertsb2.append(',');
insertsb2.append('?');
}
this.insertSQL = "INSERT INTO " + table + "(" + insertsb + ") VALUES(" + insertsb2 + ")";
this.insertSQL = "INSERT INTO " + (this.tableStrategy == null ? table : "${newtable}") + "(" + insertsb + ") VALUES(" + insertsb2 + ")";
StringBuilder updatesb = new StringBuilder();
for (String col : updatecols) {
if (updatesb.length() > 0) updatesb.append(", ");
@@ -259,6 +276,9 @@ public final class EntityInfo<T> {
if (conf == null) conf = new Properties();
this.containSQL = conf.getProperty(JDBC_CONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) > 0");
this.notcontainSQL = conf.getProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) = 0");
this.tablenotexistSqlstate = conf.getProperty(JDBC_TABLENOTEXIST_SQLSTATE, "42S02");
this.tablecopySQL = conf.getProperty(JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} LIKE ${oldtable}");
}
public void createPrimaryValue(T src) {
@@ -295,16 +315,27 @@ public final class EntityInfo<T> {
return table == null;
}
public String getInsertSQL(T bean) {
if (this.tableStrategy == null) return insertSQL;
return insertSQL.replace("${newtable}", getTable(bean));
}
public String getTable(Serializable primary) {
return table;
if (tableStrategy == null) return table;
String t = tableStrategy.getTable(table, primary);
return t == null || t.isEmpty() ? table : t;
}
public String getTable(FilterNode node) {
return table;
if (tableStrategy == null) return table;
String t = tableStrategy.getTable(table, node);
return t == null || t.isEmpty() ? table : t;
}
public String getTable(T bean) {
return table;
if (tableStrategy == null) return table;
String t = tableStrategy.getTable(table, bean);
return t == null || t.isEmpty() ? table : t;
}
public Attribute<T, Serializable> getPrimary() {