This commit is contained in:
47
src/META-INF/persistence-template.xml
Normal file
47
src/META-INF/persistence-template.xml
Normal 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&autoReconnectForPools=true&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>
|
||||
@@ -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();
|
||||
|
||||
24
src/org/redkale/source/DistributeTable.java
Normal file
24
src/org/redkale/source/DistributeTable.java
Normal 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();
|
||||
}
|
||||
29
src/org/redkale/source/DistributeTableStrategy.java
Normal file
29
src/org/redkale/source/DistributeTableStrategy.java
Normal 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);
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user