HHH-13229 - Sequences in MariaDB doesnt work on existing sequence
This commit is contained in:
parent
0750716c87
commit
d158762144
|
@ -59,7 +59,7 @@ ext {
|
|||
'jdbc.url' : 'jdbc:mysql://127.0.0.1/hibernate_orm_test?useSSL=false'
|
||||
],
|
||||
mariadb : [
|
||||
'db.dialect' : 'org.hibernate.dialect.MariaDB102Dialect',
|
||||
'db.dialect' : 'org.hibernate.dialect.MariaDB103Dialect',
|
||||
'jdbc.driver': 'org.mariadb.jdbc.Driver',
|
||||
'jdbc.user' : 'hibernate_orm_test',
|
||||
'jdbc.pass' : 'hibernate_orm_test',
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
*/
|
||||
package org.hibernate.dialect;
|
||||
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.dialect.function.StandardSQLFunction;
|
||||
import org.hibernate.tool.schema.extract.internal.SequenceNameExtractorImpl;
|
||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorMariaDBDatabaseImpl;
|
||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
|
||||
|
@ -63,8 +62,7 @@ public class MariaDB103Dialect extends MariaDB102Dialect {
|
|||
|
||||
@Override
|
||||
public SequenceInformationExtractor getSequenceInformationExtractor() {
|
||||
//TODO: Future improvement - https://hibernate.atlassian.net/browse/HHH-13008
|
||||
return SequenceNameExtractorImpl.INSTANCE;
|
||||
return SequenceInformationExtractorMariaDBDatabaseImpl.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.id.enhanced;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -568,14 +569,20 @@ public class SequenceStyleGenerator
|
|||
* @return sequence increment value
|
||||
*/
|
||||
private Long getSequenceIncrementValue(JdbcEnvironment jdbcEnvironment, String sequenceName) {
|
||||
return jdbcEnvironment.getExtractedDatabaseMetaData().getSequenceInformationList().stream().filter(
|
||||
sequenceInformation -> {
|
||||
Identifier catalog = sequenceInformation.getSequenceName().getCatalogName();
|
||||
Identifier schema = sequenceInformation.getSequenceName().getSchemaName();
|
||||
return sequenceName.equalsIgnoreCase( sequenceInformation.getSequenceName().getSequenceName().getText() ) &&
|
||||
( catalog == null || catalog.equals( jdbcEnvironment.getCurrentCatalog() ) ) &&
|
||||
( schema == null || schema.equals( jdbcEnvironment.getCurrentSchema() ) );
|
||||
}
|
||||
).map( SequenceInformation::getIncrementValue ).findFirst().orElse( null );
|
||||
return jdbcEnvironment.getExtractedDatabaseMetaData().getSequenceInformationList()
|
||||
.stream()
|
||||
.filter(
|
||||
sequenceInformation -> {
|
||||
Identifier catalog = sequenceInformation.getSequenceName().getCatalogName();
|
||||
Identifier schema = sequenceInformation.getSequenceName().getSchemaName();
|
||||
return sequenceName.equalsIgnoreCase( sequenceInformation.getSequenceName().getSequenceName().getText() ) &&
|
||||
( catalog == null || catalog.equals( jdbcEnvironment.getCurrentCatalog() ) ) &&
|
||||
( schema == null || schema.equals( jdbcEnvironment.getCurrentSchema() ) );
|
||||
}
|
||||
)
|
||||
.map( SequenceInformation::getIncrementValue )
|
||||
.filter( Objects::nonNull )
|
||||
.findFirst()
|
||||
.orElse( null );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.tool.schema.extract.internal;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.boot.model.relational.QualifiedSequenceName;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
|
||||
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
|
||||
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea, Magnus Hagström
|
||||
*/
|
||||
public class SequenceInformationExtractorMariaDBDatabaseImpl extends SequenceInformationExtractorLegacyImpl {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final SequenceInformationExtractorMariaDBDatabaseImpl INSTANCE = new SequenceInformationExtractorMariaDBDatabaseImpl();
|
||||
|
||||
// SQL to get metadata from individual sequence
|
||||
private static final String SQL_SEQUENCE_QUERY =
|
||||
"SELECT '%1$s' as sequence_name, minimum_value, maximum_value, start_value, increment, cache_size FROM %1$s ";
|
||||
|
||||
private static final String UNION_ALL =
|
||||
"UNION ALL ";
|
||||
|
||||
@Override
|
||||
public Iterable<SequenceInformation> extractMetadata(ExtractionContext extractionContext) throws SQLException {
|
||||
final String lookupSql = extractionContext.getJdbcEnvironment().getDialect().getQuerySequencesString();
|
||||
|
||||
// *should* never happen, but to be safe in the interest of performance...
|
||||
if (lookupSql == null) {
|
||||
return SequenceInformationExtractorNoOpImpl.INSTANCE.extractMetadata(extractionContext);
|
||||
}
|
||||
|
||||
final IdentifierHelper identifierHelper = extractionContext.getJdbcEnvironment().getIdentifierHelper();
|
||||
|
||||
final List<SequenceInformation> sequenceInformationList = new ArrayList<>();
|
||||
final List<String> sequenceNames = new ArrayList<>();
|
||||
|
||||
try (
|
||||
final Statement statement = extractionContext.getJdbcConnection().createStatement();
|
||||
final ResultSet resultSet = statement.executeQuery( lookupSql );
|
||||
) {
|
||||
while ( resultSet.next() ) {
|
||||
sequenceNames.add( resultSetSequenceName( resultSet ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !sequenceNames.isEmpty() ) {
|
||||
StringBuilder sequenceInfoQueryBuilder = new StringBuilder();
|
||||
|
||||
for ( String sequenceName : sequenceNames ) {
|
||||
if ( sequenceInfoQueryBuilder.length() > 0 ) {
|
||||
sequenceInfoQueryBuilder.append( UNION_ALL );
|
||||
}
|
||||
sequenceInfoQueryBuilder.append( String.format( SQL_SEQUENCE_QUERY, sequenceName ) );
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
|
||||
try (
|
||||
final Statement statement = extractionContext.getJdbcConnection().createStatement();
|
||||
final ResultSet resultSet = statement.executeQuery( sequenceInfoQueryBuilder.toString() );
|
||||
) {
|
||||
|
||||
while ( resultSet.next() ) {
|
||||
|
||||
SequenceInformation sequenceInformation = new SequenceInformationImpl(
|
||||
new QualifiedSequenceName(
|
||||
null,
|
||||
null,
|
||||
identifierHelper.toIdentifier(
|
||||
resultSetSequenceName(resultSet)
|
||||
)
|
||||
),
|
||||
resultSetStartValueSize(resultSet),
|
||||
resultSetMinValue(resultSet),
|
||||
resultSetMaxValue(resultSet),
|
||||
resultSetIncrementValue(resultSet)
|
||||
);
|
||||
|
||||
sequenceInformationList.add(sequenceInformation);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return sequenceInformationList;
|
||||
}
|
||||
|
||||
protected String resultSetSequenceName(ResultSet resultSet) throws SQLException {
|
||||
return resultSet.getString(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String sequenceCatalogColumn() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String sequenceSchemaColumn() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -18,6 +18,7 @@ import org.hibernate.dialect.DB2Dialect;
|
|||
import org.hibernate.dialect.DerbyDialect;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.HSQLDialect;
|
||||
import org.hibernate.dialect.MariaDB103Dialect;
|
||||
import org.hibernate.dialect.Oracle8iDialect;
|
||||
import org.hibernate.dialect.SQLServer2012Dialect;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
|
@ -54,6 +55,10 @@ public class SequenceValueExtractor {
|
|||
|
||||
queryString = "select " + sequenceName + ".currval from sys.dummy";
|
||||
}
|
||||
else if ( dialect instanceof MariaDB103Dialect ) {
|
||||
|
||||
queryString = "select LASTVAL(" + sequenceName + ")";
|
||||
}
|
||||
else {
|
||||
queryString = "select currval('" + sequenceName + "');";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.dialect.functional;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.MariaDB103Dialect;
|
||||
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.tool.hbm2ddl.SchemaExport;
|
||||
import org.hibernate.tool.schema.TargetType;
|
||||
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
|
||||
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-12973")
|
||||
@RequiresDialect(MariaDB103Dialect.class)
|
||||
public class SequenceInformationMariaDBTest extends
|
||||
BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private DriverManagerConnectionProviderImpl connectionProvider;
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Book.class,
|
||||
Author.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildEntityManagerFactory() {
|
||||
connectionProvider = new DriverManagerConnectionProviderImpl();
|
||||
connectionProvider.configure( Environment.getProperties() );
|
||||
|
||||
try(Connection connection = connectionProvider.getConnection();
|
||||
Statement statement = connection.createStatement()) {
|
||||
try {
|
||||
statement.execute( "DROP SEQUENCE IF EXISTS book_sequence" );
|
||||
statement.execute( "DROP SEQUENCE IF EXISTS author_sequence" );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
}
|
||||
try {
|
||||
statement.execute( "DROP TABLE TBL_BOOK" );
|
||||
statement.execute( "DROP TABLE TBL_AUTHOR" );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
}
|
||||
statement.execute( "CREATE TABLE `TBL_BOOK` ( " +
|
||||
" `ID` int(11) NOT NULL, " +
|
||||
" `TITLE` varchar(255) DEFAULT NULL, " +
|
||||
" PRIMARY KEY (`ID`) " +
|
||||
") ENGINE=InnoDB" );
|
||||
|
||||
statement.execute( "CREATE TABLE `TBL_AUTHOR` ( " +
|
||||
" `ID` int(11) NOT NULL, " +
|
||||
" `firstName` varchar(255) DEFAULT NULL, " +
|
||||
" `lastName` varchar(255) DEFAULT NULL, " +
|
||||
" PRIMARY KEY (`ID`) " +
|
||||
") ENGINE=InnoDB" );
|
||||
|
||||
statement.execute( "CREATE SEQUENCE book_sequence " +
|
||||
" START WITH 1 " +
|
||||
" INCREMENT BY 1 " +
|
||||
" MAXVALUE 2999999999 " +
|
||||
" MINVALUE 0 " +
|
||||
" CACHE 10" );
|
||||
|
||||
statement.execute( "CREATE SEQUENCE author_sequence " +
|
||||
" START WITH 1 " +
|
||||
" INCREMENT BY 1 " +
|
||||
" MAXVALUE 2999999999 " +
|
||||
" MINVALUE 0 " +
|
||||
" CACHE 10" );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
super.buildEntityManagerFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseResources() {
|
||||
super.releaseResources();
|
||||
|
||||
super.releaseResources();
|
||||
|
||||
try(Connection connection = connectionProvider.getConnection();
|
||||
Statement statement = connection.createStatement()) {
|
||||
try {
|
||||
statement.execute( "DROP SEQUENCE book_sequence" );
|
||||
statement.execute( "DROP SEQUENCE author_sequence" );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
}
|
||||
try {
|
||||
statement.execute( "DROP TABLE TBL_BOOK" );
|
||||
statement.execute( "DROP TABLE TBL_AUTHOR" );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
if ( connectionProvider != null ) {
|
||||
connectionProvider.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addMappings(Map settings) {
|
||||
settings.put( AvailableSettings.HBM2DDL_AUTO, "none" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Book book = new Book();
|
||||
book.setTitle("My Book");
|
||||
|
||||
entityManager.persist(book);
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = "TBL_BOOK")
|
||||
public static class Book {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE)
|
||||
@SequenceGenerator(
|
||||
name = "book_sequence",
|
||||
allocationSize = 10
|
||||
)
|
||||
@Column(name = "ID")
|
||||
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "TITLE")
|
||||
private String title;
|
||||
|
||||
public Book() {
|
||||
}
|
||||
|
||||
public Book(String title) {
|
||||
this.title = title;
|
||||
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Book{" +
|
||||
"id=" + id +
|
||||
", title='" + title + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = "TBL_AUTHOR")
|
||||
public static class Author {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE)
|
||||
@SequenceGenerator(
|
||||
name = "author_sequence",
|
||||
allocationSize = 10
|
||||
)
|
||||
@Column(name = "ID")
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "FIRST_NAME")
|
||||
private String firstName;
|
||||
|
||||
@Column(name = "LAST_NAME")
|
||||
private String lastName;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue