OPENJPA-2400: MariaDB support

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1501805 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jody Grassel 2013-07-10 15:43:18 +00:00
parent 8cb451d1df
commit 1b1b60c9a7
29 changed files with 713 additions and 23 deletions

View File

@ -212,6 +212,7 @@ public class JDBCConfigurationImpl
"informix", "org.apache.openjpa.jdbc.sql.InformixDictionary",
"ingres", "org.apache.openjpa.jdbc.sql.IngresDictionary",
"jdatastore", "org.apache.openjpa.jdbc.sql.JDataStoreDictionary",
"mariadb", "org.apache.openjpa.jdbc.sql.MariaDBDictionary",
"mysql", "org.apache.openjpa.jdbc.sql.MySQLDictionary",
"oracle", "org.apache.openjpa.jdbc.sql.OracleDictionary",
"pointbase", "org.apache.openjpa.jdbc.sql.PointbaseDictionary",

View File

@ -30,6 +30,7 @@ import org.apache.openjpa.conf.BrokerFactoryValue;
import org.apache.openjpa.conf.OpenJPAProductDerivation;
import org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.jdbc.sql.OracleDictionary;
import org.apache.openjpa.lib.conf.AbstractProductDerivation;
@ -74,6 +75,7 @@ public class JDBCProductDerivation extends AbstractProductDerivation
_hints.add(PREFIX + ".ResultSetType");
_hints.add(PREFIX + ".SubclassFetchMode");
_hints.add(MariaDBDictionary.SELECT_HINT);
_hints.add(MySQLDictionary.SELECT_HINT);
_hints.add(OracleDictionary.SELECT_HINT);

View File

@ -74,7 +74,7 @@ public class DBDictionaryFactory {
public static DBDictionary calculateDBDictionary(JDBCConfiguration conf,
String url, String driver, String props) {
String dclass = dictionaryClassForString(getProtocol(url), conf);
if (dclass == null)
if (dclass == null || (dclass != null && dclass.contains("MySQL")))
dclass = dictionaryClassForString(driver, conf);
if (dclass == null)
return null;
@ -90,11 +90,13 @@ public class DBDictionaryFactory {
try {
conn = ds.getConnection();
DatabaseMetaData meta = conn.getMetaData();
String dclass = dictionaryClassForString(meta
.getDatabaseProductName(), conf);
String dclass = dictionaryClassForString(meta.getDatabaseProductName(), conf);
if (dclass == null)
dclass = dictionaryClassForString(getProtocol(meta.getURL()),
conf);
dclass = dictionaryClassForString(getProtocol(meta.getURL()), conf);
if (dclass != null && dclass.contains("MySQL")) {
// MariaDB returns "MySQL" for product name, need to verify by looking at product version.
dclass = dictionaryClassForString(meta.getDatabaseProductVersion(), conf);
}
if (dclass == null)
dclass = DBDictionary.class.getName();
return newDBDictionary(conf, dclass, props, conn);
@ -225,6 +227,8 @@ public class DBDictionaryFactory {
return dbdictionaryPlugin.unalias("sqlserver");
if (prod.indexOf("jsqlconnect") != -1)
return dbdictionaryPlugin.unalias("sqlserver");
if (prod.indexOf("mariadb") != -1)
return dbdictionaryPlugin.unalias("mariadb");
if (prod.indexOf("mysql") != -1)
return dbdictionaryPlugin.unalias("mysql");
if (prod.indexOf("postgres") != -1)

View File

@ -0,0 +1,483 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.jdbc.sql;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.jdbc.identifier.DBIdentifier;
import org.apache.openjpa.jdbc.identifier.DBIdentifier.DBIdentifierType;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.kernel.JDBCStore;
import org.apache.openjpa.jdbc.kernel.exps.FilterValue;
import org.apache.openjpa.jdbc.schema.Column;
import org.apache.openjpa.jdbc.schema.ForeignKey;
import org.apache.openjpa.jdbc.schema.Index;
import org.apache.openjpa.jdbc.schema.PrimaryKey;
import org.apache.openjpa.jdbc.schema.Table;
import org.apache.openjpa.util.StoreException;
/*
* Dictionary for MariaDB, based off the MySQLDictionary.
*
*/
public class MariaDBDictionary extends DBDictionary {
public static final String SELECT_HINT = "openjpa.hint.MariaDBSelectHint";
public static final String DELIMITER_BACK_TICK = "`";
/**
* The MySQL table type to use when creating tables; defaults to innodb.
*/
public String tableType = "innodb";
/**
* Whether to use clobs; defaults to true. Set this to false if you have an
* old version of MySQL which does not handle clobs properly.
*/
public boolean useClobs = true;
/**
* Whether the driver automatically deserializes blobs.
*/
public boolean driverDeserializesBlobs = false;
/**
* Whether to inline multi-table bulk-delete operations into MySQL's
* combined <code>DELETE FROM foo, bar, baz</code> syntax.
* Defaults to false, since this may fail in the presence of InnoDB tables
* with foreign keys.
* @see http://dev.mysql.com/doc/refman/5.0/en/delete.html
*/
public boolean optimizeMultiTableDeletes = false;
public static final String tinyBlobTypeName = "TINYBLOB";
public static final String mediumBlobTypeName = "MEDIUMBLOB";
public static final String longBlobTypeName = "LONGBLOB";
public MariaDBDictionary() {
platform = "MariaDB";
validationSQL = "SELECT NOW()";
distinctCountColumnSeparator = ",";
supportsDeferredConstraints = false;
constraintNameMode = CONS_NAME_MID;
supportsMultipleNontransactionalResultSets = false;
requiresAliasForSubselect = true; // new versions
requiresTargetForDelete = true;
supportsSelectStartIndex = true;
supportsSelectEndIndex = true;
concatenateFunction = "CONCAT({0},{1})";
maxTableNameLength = 64;
maxColumnNameLength = 64;
maxIndexNameLength = 64;
maxConstraintNameLength = 64;
maxIndexesPerTable = 32;
schemaCase = SCHEMA_CASE_PRESERVE;
supportsAutoAssign = true;
lastGeneratedKeyQuery = "SELECT LAST_INSERT_ID()";
autoAssignClause = "AUTO_INCREMENT";
clobTypeName = "TEXT";
longVarcharTypeName = "TEXT";
longVarbinaryTypeName = "LONG VARBINARY";
timestampTypeName = "DATETIME";
xmlTypeName = "TEXT";
fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{
"BOOL", "LONG VARBINARY", "MEDIUMBLOB", "LONGBLOB",
"TINYBLOB", "LONG VARCHAR", "MEDIUMTEXT", "LONGTEXT", "TEXT",
"TINYTEXT", "DOUBLE PRECISION", "ENUM", "SET", "DATETIME",
}));
reservedWordSet.addAll(Arrays.asList(new String[]{
"AUTO_INCREMENT", "BINARY", "BLOB", "CHANGE", "ENUM", "INFILE",
"INT1", "INT2", "INT4", "FLOAT1", "FLOAT2", "FLOAT4", "LOAD",
"MEDIUMINT", "OUTFILE", "REPLACE", "STARTING", "TEXT", "UNSIGNED",
"ZEROFILL", "INDEX",
}));
// reservedWordSet subset that CANNOT be used as valid column names
// (i.e., without surrounding them with double-quotes)
invalidColumnWordSet.addAll(Arrays.asList(new String[]{
"ADD", "ALL", "ALTER", "AND", "AS", "ASC", "BETWEEN", "BINARY",
"BLOB", "BOTH", "BY", "CASCADE", "CASE", "CHANGE", "CHAR",
"CHARACTER", "CHECK", "COLLATE", "COLUMN", "CONSTRAINT", "CONTINUE",
"CONVERT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME",
"CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", "DEC", "DECIMAL",
"DECLARE", "DEFAULT", "DELETE", "DESC", "DESCRIBE", "DISTINCT",
"DOUBLE", "DROP", "ELSE", "END-EXEC", "EXISTS", "FALSE", "FETCH",
"FLOAT", "FLOAT4", "FOR", "FOREIGN", "FROM", "GRANT", "GROUP",
"HAVING", "IN", "INFILE", "INNER", "INSENSITIVE", "INSERT", "INT",
"INT1", "INT2", "INT4", "INTEGER", "INTERVAL", "INTO", "IS", "JOIN",
"KEY", "LEADING", "LEFT", "LIKE", "LOAD", "MATCH", "MEDIUMINT",
"NATURAL", "NOT", "NULL", "NUMERIC", "ON", "OPTION", "OR", "ORDER",
"OUTER", "OUTFILE", "PRECISION", "PRIMARY", "PROCEDURE", "READ",
"REAL", "REFERENCES", "REPLACE", "RESTRICT", "REVOKE", "RIGHT",
"SCHEMA", "SELECT", "SET", "SMALLINT", "SQL", "SQLSTATE",
"STARTING", "TABLE", "THEN", "TO", "TRAILING", "TRUE", "UNION",
"UNIQUE", "UNSIGNED", "UPDATE", "USAGE", "USING", "VALUES",
"VARCHAR", "VARYING", "WHEN", "WHERE", "WITH", "WRITE", "ZEROFILL",
"INDEX",
}));
requiresSearchStringEscapeForLike = true;
// MariaDB requires double-escape for strings
searchStringEscape = "\\\\";
typeModifierSet.addAll(Arrays.asList(new String[] { "UNSIGNED",
"ZEROFILL" }));
setLeadingDelimiter(DELIMITER_BACK_TICK);
setTrailingDelimiter(DELIMITER_BACK_TICK);
fixedSizeTypeNameSet.remove("NUMERIC");
}
@Override
public void connectedConfiguration(Connection conn) throws SQLException {
super.connectedConfiguration(conn);
DatabaseMetaData metaData = conn.getMetaData();
int maj = 0;
int min = 0;
try {
// The product version looks like 4.1.3-nt or 5.1.30
String productVersion = metaData.getDatabaseProductVersion();
int[] versions = getMajorMinorVersions(productVersion);
maj = versions[0];
min = versions[1];
} catch (IllegalArgumentException e) {
// we don't understand the version format.
// That is ok. We just take the default values.
if (log.isWarnEnabled())
log.warn(e.toString(), e);
}
supportsXMLColumn = true;
}
protected void setDelimitedCase(DatabaseMetaData metaData) {
// Determination of case sensitivity is not accurate; MariaDB JIRA CONJ-55
delimitedCase = SCHEMA_CASE_PRESERVE;
}
@Override
public Connection decorate(Connection conn) throws SQLException {
conn = super.decorate(conn);
return conn;
}
private static int[] getMajorMinorVersions(String versionStr)
throws IllegalArgumentException {
int beginIndex = 0;
versionStr = versionStr.trim();
char[] charArr = versionStr.toCharArray();
for (int i = 0; i < charArr.length; i++) {
if (Character.isDigit(charArr[i])) {
beginIndex = i;
break;
}
}
int endIndex = charArr.length;
for (int i = beginIndex+1; i < charArr.length; i++) {
if (charArr[i] != '.' && !Character.isDigit(charArr[i])) {
endIndex = i;
break;
}
}
String[] arr = versionStr.substring(beginIndex, endIndex).split("\\.");
if (arr.length < 2)
throw new IllegalArgumentException();
int maj = Integer.parseInt(arr[0]);
int min = Integer.parseInt(arr[1]);
return new int[]{maj, min};
}
@Override
public String[] getCreateTableSQL(Table table) {
String[] sql = super.getCreateTableSQL(table);
if (!StringUtils.isEmpty(tableType))
sql[0] = sql[0] + " ENGINE = " + tableType;
return sql;
}
@Override
public String[] getDropIndexSQL(Index index) {
return new String[]{ "DROP INDEX " + getFullName(index) + " ON "
+ getFullName(index.getTable(), false) };
}
/**
* Return <code>ALTER TABLE &lt;table name&gt; DROP PRIMARY KEY</code>.
*/
@Override
public String[] getDropPrimaryKeySQL(PrimaryKey pk) {
if (DBIdentifier.isNull(pk.getIdentifier()))
return new String[0];
return new String[]{ "ALTER TABLE "
+ getFullName(pk.getTable(), false)
+ " DROP PRIMARY KEY" };
}
/**
* Return <code>ALTER TABLE &lt;table name&gt; DROP FOREIGN KEY
* &lt;fk name&gt;</code>.
*/
@Override
public String[] getDropForeignKeySQL(ForeignKey fk, Connection conn) {
if (DBIdentifier.isNull(fk.getIdentifier())) {
DBIdentifier fkName = fk.loadIdentifierFromDB(this,conn);
String[] retVal = (fkName == null) ? new String[0] :
new String[]{ "ALTER TABLE "
+ getFullName(fk.getTable(), false)
+ " DROP FOREIGN KEY " + toDBName(fkName) };
return retVal;
}
return new String[]{ "ALTER TABLE "
+ getFullName(fk.getTable(), false)
+ " DROP FOREIGN KEY " + toDBName(fk.getIdentifier()) };
}
@Override
public String[] getAddPrimaryKeySQL(PrimaryKey pk) {
String[] sql = super.getAddPrimaryKeySQL(pk);
// mysql requires that a column be declared NOT NULL before
// it can be made a primary key.
Column[] cols = pk.getColumns();
String[] ret = new String[cols.length + sql.length];
for (int i = 0; i < cols.length; i++) {
ret[i] = "ALTER TABLE " + getFullName(cols[i].getTable(), false)
+ " CHANGE " + toDBName(cols[i].getIdentifier())
+ " " + toDBName(cols[i].getIdentifier()) // name twice
+ " " + getTypeName(cols[i]) + " NOT NULL";
}
System.arraycopy(sql, 0, ret, cols.length, sql.length);
return ret;
}
@Override
public String[] getDeleteTableContentsSQL(Table[] tables,Connection conn) {
// mysql >= 4 supports more-optimal delete syntax
if (!optimizeMultiTableDeletes)
return super.getDeleteTableContentsSQL(tables,conn);
else {
StringBuilder buf = new StringBuilder(tables.length * 8);
buf.append("DELETE FROM ");
for (int i = 0; i < tables.length; i++) {
buf.append(toDBName(tables[i].getFullIdentifier()));
if (i < tables.length - 1)
buf.append(", ");
}
return new String[] { buf.toString() };
}
}
@Override
protected void appendSelectRange(SQLBuffer buf, long start, long end,
boolean subselect) {
buf.append(" LIMIT ").appendValue(start).append(", ");
if (end == Long.MAX_VALUE)
buf.appendValue(Long.MAX_VALUE);
else
buf.appendValue(end - start);
}
@Override
protected Column newColumn(ResultSet colMeta)
throws SQLException {
Column col = super.newColumn(colMeta);
if (col.isNotNull() && "0".equals(col.getDefaultString()))
col.setDefaultString(null);
return col;
}
@Override
public Object getBlobObject(ResultSet rs, int column, JDBCStore store)
throws SQLException {
// if the user has set a get-blob strategy explicitly or the driver
// does not automatically deserialize, delegate to super
if (useGetBytesForBlobs || useGetObjectForBlobs || !driverDeserializesBlobs)
return super.getBlobObject(rs, column, store);
// most mysql drivers deserialize on getObject
return rs.getObject(column);
}
@Override
public int getPreferredType(int type) {
if (type == Types.CLOB && !useClobs)
return Types.LONGVARCHAR;
return super.getPreferredType(type);
}
/**
* Append XML comparison.
*
* @param buf the SQL buffer to write the comparison
* @param op the comparison operation to perform
* @param lhs the left hand side of the comparison
* @param rhs the right hand side of the comparison
* @param lhsxml indicates whether the left operand maps to XML
* @param rhsxml indicates whether the right operand maps to XML
*/
@Override
public void appendXmlComparison(SQLBuffer buf, String op, FilterValue lhs,
FilterValue rhs, boolean lhsxml, boolean rhsxml) {
super.appendXmlComparison(buf, op, lhs, rhs, lhsxml, rhsxml);
if (lhsxml)
appendXmlValue(buf, lhs);
else
lhs.appendTo(buf);
buf.append(" ").append(op).append(" ");
if (rhsxml)
appendXmlValue(buf, rhs);
else
rhs.appendTo(buf);
}
/**
* Append XML column value so that it can be used in comparisons.
*
* @param buf the SQL buffer to write the value
* @param val the value to be written
*/
private void appendXmlValue(SQLBuffer buf, FilterValue val) {
buf.append("ExtractValue(").
append(val.getColumnAlias(val.getFieldMapping().getColumns()[0])).
append(",'/*/");
val.appendTo(buf);
buf.append("')");
}
@Override
public int getBatchFetchSize(int batchFetchSize) {
return Integer.MIN_VALUE;
}
/**
* Check to see if we have set the {@link #SELECT_HINT} in the
* fetch configuration, and if so, append the MySQL hint after the
* "SELECT" part of the query.
*/
@Override
public String getSelectOperation(JDBCFetchConfiguration fetch) {
Object hint = fetch == null ? null : fetch.getHint(SELECT_HINT);
String select = "SELECT";
if (hint != null)
select += " " + hint;
return select;
}
@Override
protected Collection<String> getSelectTableAliases(Select sel) {
Set<String> result = new HashSet<String>();
List<String> selects = sel.getIdentifierAliases();
for (String s : selects) {
String tableAlias = s.substring(0, s.indexOf('.'));
result.add(tableAlias);
}
return result;
}
@Override
protected int matchErrorState(Map<Integer,Set<String>> errorStates, SQLException ex) {
int state = super.matchErrorState(errorStates, ex);
if (state == StoreException.GENERAL &&
ex.getNextException() != null &&
"JZ0002".equalsIgnoreCase(ex.getNextException().getSQLState())) {
if (conf != null && conf.getLockTimeout() != -1) {
state = StoreException.LOCK;
} else {
state = StoreException.QUERY;
}
}
return state;
}
@Override
public boolean isFatalException(int subtype, SQLException ex) {
if ((subtype == StoreException.LOCK && ex.getErrorCode() == 1205)
||(subtype == StoreException.LOCK && "JZ0002".equalsIgnoreCase(ex.getSQLState()))
||(subtype == StoreException.QUERY && ex.getErrorCode() == 1317)) {
return false;
}
if (ex.getErrorCode() == 0 && ex.getSQLState() == null)
return false;
return super.isFatalException(subtype, ex);
}
/**
* OPENJPA-740 Special case for MySql special column types,
* like LONGTEXT, LONGBLOG etc..
* @see org.apache.openjpa.jdbc.sql.DBDictionary#getTypeName(org.apache.openjpa.jdbc.schema.Column)
*/
@Override
public String getTypeName(Column col) {
// handle blobs differently, if the DBItentifierType is NULL (e.g. no column definition is set).
if (col.getType() == Types.BLOB && col.getTypeIdentifier().getType() == DBIdentifierType.NULL) {
if (col.getSize() <= 0) // unknown size
return blobTypeName; // return old default of 64KB
else if (col.getSize() <= 255)
return tinyBlobTypeName;
else if (col.getSize() <= 65535)
return blobTypeName; // old default of 64KB
else if (col.getSize() <= 16777215)
return mediumBlobTypeName;
else
return longBlobTypeName;
} else {
return super.getTypeName(col);
}
}
@Override
public void indexOf(SQLBuffer buf, FilterValue str, FilterValue find,
FilterValue start) {
buf.append("LOCATE(");
find.appendTo(buf);
buf.append(", ");
str.appendTo(buf);
if (start != null) {
buf.append(", ");
start.appendTo(buf);
}
buf.append(")");
}
}

View File

@ -206,7 +206,8 @@ ConnectionDriverName-values: org.hsqldb.jdbcDriver,org.hsql.jdbcDriver,\
weblogic.jdbc.mssqlserver4.Driver,com.mysql.jdbc.DatabaseMetaData,\
org.gjt.mm.mysql.Driver,com.sap.dbtech.jdbc.DriverSapDB,\
com.sybase.jdbc2.jdbc.SybDriver,com.sybase.jdbc.SybDriver,\
com.internetcds.jdbc.tds.Driver,weblogic.jdbc.pool.Driver
com.internetcds.jdbc.tds.Driver,weblogic.jdbc.pool.Driver,\
org.mariadb.jdbc.Driver,org.mariadb.jdbc.DatabaseMetaData
ConnectionURL-values: jdbc:JSQLConnect://<hostname>/database=<database>,\
jdbc:cloudscape:<database>;create=true,\
jdbc:twtds:sqlserver://<hostname>/<database>,\

View File

@ -162,7 +162,16 @@
<optimistic></optimistic>
<query>70100</query>
</dictionary>
<dictionary class="org.apache.openjpa.jdbc.sql.MariaDBDictionary">
<lock>41000</lock>
<referential-integrity>630,839,840,893,1062,1169,1215,1216,1217,1451,1452,1557</referential-integrity>
<object-exists>23000</object-exists>
<object-not-found></object-not-found>
<optimistic></optimistic>
<query>70100</query>
</dictionary>
<dictionary class="org.apache.openjpa.jdbc.sql.OracleDictionary">
<lock>42000,61000,72000</lock>
<referential-integrity>1,1400,1722,2291,2292</referential-integrity>

View File

@ -118,6 +118,35 @@
<dbcp.minIdle>0</dbcp.minIdle>
</properties>
</profile>
<!-- Profile for testing with MariaDB DB -->
<profile>
<id>test-mariadb</id>
<activation>
<property>
<name>test-mariadb</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>mariadb</groupId>
<artifactId>mariadb-connector-java</artifactId>
<version>${mariadb.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<connection.driver.name>org.mariadb.jdbc.Driver</connection.driver.name>
<!--<connection.url>jdbc:mysql://localhost/OPENJPA</connection.url>-->
<connection.url>${openjpa.mariadb.url}</connection.url>
<connection.username>${openjpa.mariadb.username}</connection.username>
<connection.password>${openjpa.mariadb.password}</connection.password>
<jdbc.DBDictionary />
<!-- DBCP overrides for MariaDB testing -->
<dbcp.maxIdle>0</dbcp.maxIdle>
<dbcp.minIdle>0</dbcp.minIdle>
</properties>
</profile>
<!-- Profile for testing with PostgreSQL DB -->
<profile>

View File

@ -29,6 +29,7 @@ import org.apache.openjpa.conf.OpenJPAProductDerivation;
import org.apache.openjpa.conf.Specification;
import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
import org.apache.openjpa.jdbc.kernel.JDBCStoreManager;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.jdbc.sql.OracleDictionary;
import org.apache.openjpa.lib.conf.AbstractProductDerivation;
@ -127,6 +128,7 @@ public class JDBCPersistenceProductDerivation
_hints.add("openjpa.FetchPlan.ResultSetType");
_hints.add("openjpa.FetchPlan.SubclassFetchMode");
_hints.add(MariaDBDictionary.SELECT_HINT);
_hints.add(MySQLDictionary.SELECT_HINT);
_hints.add(OracleDictionary.SELECT_HINT);

View File

@ -21,6 +21,7 @@ package org.apache.openjpa.conf;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.jdbc.sql.OracleDictionary;
import org.apache.openjpa.kernel.QueryHints;
@ -51,6 +52,7 @@ public class TestQueryHints extends SingleEMFTestCase {
public void testSupportedHintsContainProductDerivationHints() {
assertSupportedHint(OracleDictionary.SELECT_HINT, true);
assertSupportedHint(MySQLDictionary.SELECT_HINT, true);
assertSupportedHint(MariaDBDictionary.SELECT_HINT, true);
}
public void testSupportedHintsContainFetchPlanHints() {

View File

@ -31,6 +31,7 @@ import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.datacache.DataCachePCData;
import org.apache.openjpa.jdbc.sql.DB2Dictionary;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.jdbc.sql.OracleDictionary;
import org.apache.openjpa.jdbc.sql.SQLServerDictionary;

View File

@ -23,6 +23,7 @@ import org.apache.openjpa.jdbc.identifier.DBIdentifier.DBIdentifierType;
import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.schema.Column;
import org.apache.openjpa.jdbc.schema.Table;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
@ -41,7 +42,7 @@ public class TestBlobColumnType extends SingleEMFTestCase {
// need to do this without BlobColumnEntity.class since it contains a column definition which might
// not work with all databases.
super.setUp((Object) null);
if (!(getDBDictionary() instanceof MySQLDictionary)) {
if (!(getDBDictionary() instanceof MySQLDictionary || getDBDictionary() instanceof MariaDBDictionary)) {
// normal teardown will take care of the EMF.
return;
}

View File

@ -106,6 +106,7 @@ public abstract class AbstractTestCase extends AbstractCachedEMFTestCase {
EMPRESS,
HYPERSONIC,
POSTGRESQL,
MARIADB,
MYSQL,
SQLSERVER,
DB2,

View File

@ -37,6 +37,7 @@ import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.DerbyDictionary;
import org.apache.openjpa.jdbc.sql.HSQLDictionary;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.jdbc.sql.OracleDictionary;
import org.apache.openjpa.lib.jdbc.JDBCListener;
@ -186,7 +187,7 @@ public abstract class AbstractCriteriaTestCase extends TestCase {
cSQL.size());
}
if (!(dict instanceof DerbyDictionary || dict instanceof MySQLDictionary))
if (!(dict instanceof DerbyDictionary || dict instanceof MySQLDictionary || dict instanceof MariaDBDictionary))
return;
for (int i = 0; i < jSQL.size(); i++) {
@ -219,7 +220,7 @@ public abstract class AbstractCriteriaTestCase extends TestCase {
fail("JPQL " + jpql + " failed to execute\r\n" + w);
}
if (!(dict instanceof DerbyDictionary || dict instanceof MySQLDictionary))
if (!(dict instanceof DerbyDictionary || dict instanceof MySQLDictionary || dict instanceof MariaDBDictionary))
return;
for (int i = 0; i < jSQL.size(); i++) {
@ -242,7 +243,7 @@ public abstract class AbstractCriteriaTestCase extends TestCase {
fail(w.toString());
}
if (!(dict instanceof DerbyDictionary || dict instanceof MySQLDictionary))
if (!(dict instanceof DerbyDictionary || dict instanceof MySQLDictionary || dict instanceof MariaDBDictionary))
return;
String jSql = jSQL.get(0).trim();

View File

@ -20,6 +20,7 @@ package org.apache.openjpa.persistence.delimited.identifiers.noschema;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.test.SQLListenerTestCase;
@ -35,6 +36,7 @@ public class TestNoSchemaManualDelimIdSeqGen extends SQLListenerTestCase {
@Override
public void setUp() throws Exception {
setUnsupportedDatabases(MariaDBDictionary.class);
setUnsupportedDatabases(MySQLDictionary.class);
if (isTestsDisabled())
return;

View File

@ -24,6 +24,7 @@ import javax.persistence.Query;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.test.SQLListenerTestCase;
@ -42,6 +43,7 @@ public class TestNoSchemaManualDelimitedJoinAnnotations extends SQLListenerTestC
@Override
public void setUp() throws Exception {
setUnsupportedDatabases(MariaDBDictionary.class);
setUnsupportedDatabases(MySQLDictionary.class);
if (isTestsDisabled())
return;

View File

@ -116,6 +116,18 @@ public class TestDynamicSchemas extends SingleEMFTestCase {
closeEMF(sqlserverEMF);
}
public void testMariaDBDynamicSchema() {
OpenJPAEntityManagerFactorySPI mysqlEMF =
createEMF(EntityVeryLongNames.class, EntityReservedWords.class,
"openjpa.ConnectionURL",
"jdbc:mysql://host1:1,host2:2/database?p1=v1&p2=v2",
"openjpa.jdbc.SynchronizeMappings", "export",
"openjpa.jdbc.SchemaFactory", "dynamic", RETAIN_DATA);
validateTableName( mysqlEMF );
closeEMF(mysqlEMF);
}
public void testMySQLDynamicSchema() {
OpenJPAEntityManagerFactorySPI mysqlEMF =

View File

@ -28,6 +28,7 @@ import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.H2Dictionary;
import org.apache.openjpa.jdbc.sql.HSQLDictionary;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.jdbc.sql.OracleDictionary;
import org.apache.openjpa.jdbc.sql.PostgresDictionary;
@ -46,6 +47,7 @@ public class TestMultipleSchemaNames extends SingleEMFTestCase {
// Need to skip tests on some databases.
// See createSchemas() comment at the bottom
setUnsupportedDatabases(
MariaDBDictionary.class,
MySQLDictionary.class,
OracleDictionary.class,
SQLServerDictionary.class,

View File

@ -25,6 +25,7 @@ import javax.persistence.Query;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.jdbc.sql.OracleDictionary;
import org.apache.openjpa.persistence.jdbc.query.domain.TimeKeeper;
@ -47,9 +48,11 @@ public class TestHintedQuery extends SQLListenerTestCase {
em.getTransaction().commit();
String jpql = "SELECT tk FROM TimeKeeper tk";
String mariadbHint = "SQL_NO_CACHE";
String mysqlHint = "SQL_NO_CACHE";
String oracleHint = "/*+ first_rows(100) */";
Query query = em.createQuery(jpql);
query.setHint(MariaDBDictionary.SELECT_HINT, mariadbHint);
query.setHint(MySQLDictionary.SELECT_HINT, mysqlHint);
query.setHint(OracleDictionary.SELECT_HINT, oracleHint);
List keepers = query.getResultList();
@ -59,6 +62,10 @@ public class TestHintedQuery extends SQLListenerTestCase {
// Other dictionaries should ignore them.
DBDictionary dict = ((JDBCConfiguration) emf.getConfiguration())
.getDBDictionaryInstance();
if (dict instanceof MariaDBDictionary) {
assertContainsSQL("SELECT " + mariadbHint + " ");
return;
}
if (dict instanceof MySQLDictionary) {
assertContainsSQL("SELECT " + mysqlHint + " ");
return;
@ -67,6 +74,7 @@ public class TestHintedQuery extends SQLListenerTestCase {
assertContainsSQL("SELECT " + oracleHint + " ");
return;
}
assertNotSQL(".*" + mariadbHint + ".*");
assertNotSQL(".*" + mysqlHint + ".*");
assertNotSQL(".*" + oracleHint + ".*");
}

View File

@ -56,7 +56,8 @@ public class TestLazySchemaFactory
// kodo uses
JDBCConfiguration conf = (JDBCConfiguration) getConfiguration();
_fks = conf.getDBDictionaryInstance().supportsForeignKeys
&& getCurrentPlatform() != AbstractTestCase.Platform.MYSQL;
&& getCurrentPlatform() != AbstractTestCase.Platform.MYSQL
&& getCurrentPlatform() != AbstractTestCase.Platform.MARIADB;
LazySchemaFactory factory = new LazySchemaFactory();
factory.setConfiguration(conf);

View File

@ -27,6 +27,7 @@ import javax.persistence.EntityManager;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
import org.apache.openjpa.persistence.jdbc.SQLSniffer;
@ -78,7 +79,7 @@ public class TestNamedUniqueConstraint extends SQLListenerTestCase {
}
private String getUniqueConstraint(String unique) {
if (dict instanceof MySQLDictionary) {
if (dict instanceof MySQLDictionary || dict instanceof MariaDBDictionary) {
//CREATE TABLE N_UNIQUE_A (aid INTEGER NOT NULL, f1 INTEGER NOT NULL, f2 INTEGER NOT NULL,
//f3 INTEGER NOT NULL, f4 INTEGER NOT NULL, f5 INTEGER, f6 INTEGER, PRIMARY KEY (aid),
//UNIQUE U_N_UNQU__F1 (f1),

View File

@ -25,6 +25,7 @@ import javax.persistence.EntityManager;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
import org.apache.openjpa.persistence.jdbc.SQLSniffer;
@ -79,7 +80,7 @@ public class TestNamedUniqueConstraintWithXMLDescriptor extends SQLListenerTestC
}
private String getUniqueConstraint(String unique) {
if (dict instanceof MySQLDictionary) {
if (dict instanceof MySQLDictionary || dict instanceof MariaDBDictionary) {
//CREATE TABLE N_UNIQUE_A (aid INTEGER NOT NULL, f1 INTEGER NOT NULL, f2 INTEGER NOT NULL,
//f3 INTEGER NOT NULL, f4 INTEGER NOT NULL, f5 INTEGER, f6 INTEGER, PRIMARY KEY (aid),
//UNIQUE U_N_UNQU__F1 (f1),

View File

@ -122,6 +122,7 @@ public class TestSpecialNumbers extends BaseKernelTest {
} catch (Exception e) {
bug(EnumSet.of(AbstractTestCase.Platform.POSTGRESQL,
AbstractTestCase.Platform.DB2,
AbstractTestCase.Platform.MARIADB,
AbstractTestCase.Platform.MYSQL,
AbstractTestCase.Platform.DERBY), 494, e,
"Some datastores cannot store Float.MAX_VALUE");
@ -142,7 +143,7 @@ public class TestSpecialNumbers extends BaseKernelTest {
AbstractTestCase.Platform.DERBY), 494, e,
"Some databases cannot store Float.MIN_VALUE");
} catch (AssertionFailedError e) {
bug(EnumSet.of(AbstractTestCase.Platform.MYSQL,
bug(EnumSet.of(AbstractTestCase.Platform.MARIADB, AbstractTestCase.Platform.MYSQL,
AbstractTestCase.Platform.SQLSERVER), 494, e,
"Some databases cannot store Float.MIN_VALUE");
}
@ -167,6 +168,7 @@ public class TestSpecialNumbers extends BaseKernelTest {
} catch (Exception e) {
bug(EnumSet.of(AbstractTestCase.Platform.POINTBASE,
AbstractTestCase.Platform.POSTGRESQL,
AbstractTestCase.Platform.MARIADB,
AbstractTestCase.Platform.MYSQL,
AbstractTestCase.Platform.DB2,
AbstractTestCase.Platform.ORACLE,
@ -186,6 +188,7 @@ public class TestSpecialNumbers extends BaseKernelTest {
} catch (Exception e) {
bug(EnumSet.of(AbstractTestCase.Platform.POINTBASE,
AbstractTestCase.Platform.POSTGRESQL,
AbstractTestCase.Platform.MARIADB,
AbstractTestCase.Platform.MYSQL,
AbstractTestCase.Platform.DB2,
AbstractTestCase.Platform.ORACLE,
@ -204,6 +207,7 @@ public class TestSpecialNumbers extends BaseKernelTest {
new Double(Double.MAX_VALUE));
} catch (Exception e) {
bug(EnumSet.of(AbstractTestCase.Platform.POINTBASE,
AbstractTestCase.Platform.MARIADB,
AbstractTestCase.Platform.MYSQL,
AbstractTestCase.Platform.ORACLE,
AbstractTestCase.Platform.POSTGRESQL,
@ -231,7 +235,7 @@ public class TestSpecialNumbers extends BaseKernelTest {
AbstractTestCase.Platform.DERBY), 494, e,
"Some databases cannot store Double.MIN_VALUE");
} catch (AssertionFailedError e) {
bug(AbstractTestCase.Platform.MYSQL, 494, e,
bug(EnumSet.of(AbstractTestCase.Platform.MARIADB, AbstractTestCase.Platform.MYSQL), 494, e,
"Some databases cannot store Double.MIN_VALUE");
}
}

View File

@ -195,7 +195,7 @@ public class TestSpecialNumbers2 extends BaseKernelTest {
AbstractTestCase.Platform.DERBY), 494, e,
"Some databases cannot store Double.MIN_VALUE");
} catch (AssertionFailedError e) {
bug(AbstractTestCase.Platform.MYSQL, 494, e,
bug(EnumSet.of(AbstractTestCase.Platform.MARIADB, AbstractTestCase.Platform.MYSQL), 494, e,
"Some databases cannot store Double.MIN_VALUE");
}
}

View File

@ -124,7 +124,7 @@ public abstract class GroupingTestCase
"from AllFieldTypes o"
+ " group by o.intField order by sum(o.shortField) asc");
prepareQuery(q);
// this might fail in MySQL
// this might fail in MySQL/MariaDB
List res = q.getResultList();
assertEquals(2, res.size());
Iterator itr = res.iterator();

View File

@ -26,6 +26,7 @@ import javax.persistence.Query;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.MariaDBDictionary;
import org.apache.openjpa.jdbc.sql.MySQLDictionary;
import org.apache.openjpa.persistence.query.Customer.CreditRating;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
@ -255,7 +256,7 @@ public class TestSubquery
// this at the bottom of the UPDATE documentation(http://dev.mysql.com/doc/refman/5.0/en/update.html):
// Currently, you cannot update a table and select from the same table in a subquery.
if (dict instanceof MySQLDictionary)
if (dict instanceof MySQLDictionary || dict instanceof MariaDBDictionary)
return;
em.getTransaction().begin();

View File

@ -127,6 +127,34 @@
<jdbc.DBDictionary />
</properties>
</profile>
<!-- Profile for testing with MariaDB DB -->
<profile>
<id>test-mariadb</id>
<activation>
<property>
<name>test-mariadb</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>mariadb</groupId>
<artifactId>mariadb-connector-java</artifactId>
<version>${mariadb.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<connection.driver.name>org.mariadb.jdbc.Driver</connection.driver.name>
<connection.url>${openjpa.mariadb.url}</connection.url>
<connection.username>${openjpa.mariadb.username}</connection.username>
<connection.password>${openjpa.mariadb.password}</connection.password>
<!-- DBCP overrides for MariaDB testing -->
<dbcp.maxIdle>0</dbcp.maxIdle>
<dbcp.minIdle>0</dbcp.minIdle>
<jdbc.DBDictionary />
</properties>
</profile>
<!-- Profile for testing with PostgreSQL DB -->
<profile>

View File

@ -121,8 +121,8 @@ public abstract class LockScopeTestCase extends SQLListenerTestCase {
}
protected enum DBType {
access, db2, derby, empress, foxpro, h2, hsql, informix, ingres, jdatastore, mysql, oracle, pointbase, postgres,
sqlserver, sybase
access, db2, derby, empress, foxpro, h2, hsql, informix, ingres, jdatastore, mariadb, mysql, oracle, pointbase,
postgres, sqlserver, sybase
};
protected DBType getDBType(EntityManager em) {

View File

@ -1040,8 +1040,8 @@ public abstract class SequencedActionsTest extends SQLListenerTestCase {
}
protected enum DBType {
access, db2, derby, empress, foxpro, h2, hsql, informix, ingres, jdatastore, mysql, oracle, pointbase, postgres,
sqlserver, sybase
access, db2, derby, empress, foxpro, h2, hsql, informix, ingres, jdatastore, mariadb, mysql, oracle, pointbase,
postgres, sqlserver, sybase
};
protected DBType getDBType(EntityManager em) {

View File

@ -156,6 +156,29 @@
</properties>
</persistence-unit>
<persistence-unit name="mariadb">
<properties>
<property name="openjpa.BrokerFactory" value="slice"/>
<property name="openjpa.ConnectionDriverName" value="org.mariadb.jdbc.Driver"/>
<property name="openjpa.slice.Names" value="One,Two"/>
<property name="openjpa.slice.DistributionPolicy" value="org.apache.openjpa.slice.policy.UserDistributionPolicy"/>
<property name="openjpa.ConnectionUserName" value="root"/>
<property name="openjpa.ConnectionPassword" value=""/>
<property name="openjpa.slice.One.ConnectionDriverName" value="org.mariadb.jdbc.Driver"/>
<property name="openjpa.slice.One.ConnectionURL" value="jdbc:mysql://localhost/slice1"/>
<property name="openjpa.slice.Two.ConnectionDriverName" value="org.mariadb.jdbc.Driver"/>
<property name="openjpa.slice.Two.ConnectionURL" value="jdbc:mysql://localhost/slice2"/>
<property name="openjpa.Multithreaded" value="false"/>
<property name="openjpa.jdbc.SynchronizeMappings" value="refresh"/>
<property name="openjpa.jdbc.MappingDefaults" value="DefaultMissingInfo=true"/>
<property name="openjpa.RuntimeUnenhancedClasses" value="unsupported"/>
<property name="openjpa.DynamicEnhancementAgent" value="false"/>
</properties>
</persistence-unit>
<persistence-unit name="replication">
<class>org.apache.openjpa.slice.ReplicatedParent</class>
<class>org.apache.openjpa.slice.ReplicatedChild</class>
@ -231,6 +254,27 @@
</properties>
</persistence-unit>
<persistence-unit name="car.mariadb">
<properties>
<property name="openjpa.BrokerFactory" value="slice"/>
<property name="openjpa.ConnectionDriverName" value="org.mariadb.jdbc.Driver"/>
<property name="openjpa.slice.Names" value="BMW,Honda,Ford"/>
<property name="openjpa.ConnectionUserName" value="root"/>
<property name="openjpa.ConnectionPassword" value=""/>
<property name="openjpa.slice.BMW.ConnectionURL" value="jdbc:mysql://localhost/bmw"/>
<property name="openjpa.slice.Honda.ConnectionURL" value="jdbc:mysql://localhost/ford"/>
<property name="openjpa.slice.Ford.ConnectionURL" value="jdbc:mysql://localhost/honda"/>
<property name="openjpa.Multithreaded" value="false"/>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=native)"/>
<property name="openjpa.jdbc.MappingDefaults" value="DefaultMissingInfo=true"/>
<property name="openjpa.RuntimeUnenhancedClasses" value="unsupported"/>
<property name="openjpa.DynamicEnhancementAgent" value="false"/>
</properties>
</persistence-unit>
<persistence-unit name="perf.mysql.slice">
<class>org.apache.openjpa.slice.Person</class>
<class>org.apache.openjpa.slice.Address</class>
@ -277,4 +321,51 @@
<property name="openjpa.DynamicEnhancementAgent" value="false"/>
</properties>
</persistence-unit>
<persistence-unit name="perf.mariadb.slice">
<class>org.apache.openjpa.slice.Person</class>
<class>org.apache.openjpa.slice.Address</class>
<class>org.apache.openjpa.slice.Country</class>
<properties>
<property name="openjpa.BrokerFactory" value="slice"/>
<property name="openjpa.ConnectionDriverName" value="org.apache.commons.dbcp.BasicDataSource"/>
<property name="openjpa.slice.Names" value="S1,S2,S3,S4"/>
<property name="openjpa.ConnectionUserName" value="root"/>
<property name="openjpa.ConnectionPassword" value=""/>
<property name="openjpa.slice.S1.ConnectionProperties" value="DriverClassName=org.mariadb.jdbc.Driver,Url=jdbc:mysql://localhost/S1,MaxActive=4"/>
<property name="openjpa.slice.S2.ConnectionProperties" value="DriverClassName=org.mariadb.jdbc.Driver,Url=jdbc:mysql://localhost/S2,MaxActive=4"/>
<property name="openjpa.slice.S3.ConnectionProperties" value="DriverClassName=org.mariadb.jdbc.Driver,Url=jdbc:mysql://localhost/S3,MaxActive=4"/>
<property name="openjpa.slice.S4.ConnectionProperties" value="DriverClassName=org.mariadb.jdbc.Driver,Url=jdbc:mysql://localhost/S4,MaxActive=4"/>
<property name="openjpa.jdbc.DBDictionary" value="mariadb"/>
<property name="openjpa.Multithreaded" value="false"/>
<property name="openjpa.jdbc.SynchronizeMappings" value="refresh"/>
<property name="openjpa.jdbc.MappingDefaults" value="DefaultMissingInfo=true"/>
<property name="openjpa.RuntimeUnenhancedClasses" value="unsupported"/>
<property name="openjpa.DynamicEnhancementAgent" value="false"/>
</properties>
</persistence-unit>
<persistence-unit name="perf.mariadb.mono">
<class>org.apache.openjpa.slice.Person</class>
<class>org.apache.openjpa.slice.Address</class>
<class>org.apache.openjpa.slice.Country</class>
<properties>
<property name="openjpa.ConnectionDriverName" value="org.apache.commons.dbcp.BasicDataSource"/>
<property name="openjpa.ConnectionUserName" value="root"/>
<property name="openjpa.ConnectionPassword" value=""/>
<property name="openjpa.ConnectionProperties" value="DriverClassName=org.mariadb.jdbc.Driver,Url=jdbc:mysql://localhost/S,MaxActive=4"/>
<property name="openjpa.jdbc.DBDictionary" value="mariadb"/>
<property name="openjpa.Multithreaded" value="false"/>
<property name="openjpa.jdbc.SynchronizeMappings" value="refresh"/>
<property name="openjpa.jdbc.MappingDefaults" value="DefaultMissingInfo=true"/>
<property name="openjpa.RuntimeUnenhancedClasses" value="unsupported"/>
<property name="openjpa.DynamicEnhancementAgent" value="false"/>
</properties>
</persistence-unit>
</persistence>