Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/main/java/org/javawebstack/orm/TableInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class TableInfo {
private String relationField;
private final Map<String, String> filterable = new HashMap<>();
private final List<String> searchable = new ArrayList<>();
private final List<Index> indices = new ArrayList<>();

private static final Class<?>[] appliesDefaultSize = {
String.class,
Expand Down Expand Up @@ -105,6 +106,13 @@ private void analyzeTable(Class<? extends Model> model) throws ORMConfigurationE
if (!fields.containsKey(dates.update()))
throw new ORMConfigurationException("Missing dates field '" + dates.update() + "'");
}

Index[] unfilteredIndices = model.getAnnotationsByType(Index.class);
for (Index index : unfilteredIndices) {
if (index.value().length == 0)
continue;
indices.add(index);
}
}

private void analyzeColumns(Class<? extends Model> model) throws ORMConfigurationException {
Expand Down Expand Up @@ -292,4 +300,7 @@ public String getRelationField() {
return relationField;
}

public List<Index> getIndices() {
return indices;
}
}
25 changes: 25 additions & 0 deletions src/main/java/org/javawebstack/orm/annotation/Index.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.javawebstack.orm.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(Index.Indices.class)
public @interface Index {
Type type() default Type.AUTO;
boolean unique() default false;
String id() default "";
String[] value() default {};

enum Type {
AUTO,
BTREE,
HASH
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Indices {
Index[] value();
}
}
40 changes: 40 additions & 0 deletions src/main/java/org/javawebstack/orm/migration/AutoMigrator.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.javawebstack.orm.Repo;
import org.javawebstack.orm.TableInfo;
import org.javawebstack.orm.annotation.Index;
import org.javawebstack.orm.exception.ORMQueryException;
import org.javawebstack.orm.wrapper.SQL;

Expand All @@ -12,6 +13,7 @@
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class AutoMigrator {

Expand Down Expand Up @@ -122,6 +124,31 @@ private static void migrateTable(SQL sql, TableInfo info, boolean tableExists) {
}
}
}

List<String> existingIndices = getIndices(sql, info.getTableName());
for (Index index : info.getIndices()) {
String columns = Stream.of(index.value()).map(info::getColumnName).map(s -> "`" + s + "`").collect(Collectors.joining(","));
String id = index.id().length() > 0 ? index.id() : "idx_" + String.join("_", index.value());
if (existingIndices.contains(id))
continue;

StringBuilder sb = new StringBuilder("CREATE ");
if (index.unique())
sb.append("UNIQUE ");
sb.append("INDEX `").append(id).append("` ");
if (index.type() != Index.Type.AUTO)
sb.append("USING ").append(index.type().name()).append(" ");
sb.append("ON `")
.append(info.getTableName())
.append("` (")
.append(columns)
.append(");");
try {
sql.write(sb.toString());
} catch (SQLException throwables) {
throw new ORMQueryException(throwables);
}
}
}

private static Map<String, String> getColumnKeys(SQL sql, String tableName) {
Expand All @@ -137,6 +164,19 @@ private static Map<String, String> getColumnKeys(SQL sql, String tableName) {
}
}

private static List<String> getIndices(SQL sql, String tableName) {
try {
List<String> indices = new ArrayList<>();
ResultSet rs = sql.read("SHOW INDEX FROM `" + tableName + "`;");
while (rs.next()) {
indices.add(rs.getString(1));
}
return indices;
} catch (SQLException throwables) {
throw new ORMQueryException(throwables);
}
}

private static List<String> getTables(SQL sql) {
try {
List<String> tables = new ArrayList<>();
Expand Down
36 changes: 36 additions & 0 deletions src/test/java/org/javawebstack/orm/test/automigrate/IndexTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.javawebstack.orm.test.automigrate;

import org.javawebstack.orm.ORM;
import org.javawebstack.orm.ORMConfig;
import org.javawebstack.orm.exception.ORMConfigurationException;
import org.javawebstack.orm.test.ORMTestCase;
import org.javawebstack.orm.test.shared.models.IndexType;
import org.javawebstack.orm.test.shared.verification.Indexes;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.sql.SQLException;

public class IndexTest extends ORMTestCase {

@BeforeEach
public void setUp() throws ORMConfigurationException {
ORMConfig config = new ORMConfig()
.setDefaultSize(255);
ORM.register(IndexType.class, sql(), config);
ORM.autoMigrate(true);
}

@Test
public void hasIndexes() throws SQLException {
Indexes indexes = new Indexes("index_types");
indexes.assertHasIndex("idx_key_value"); // auto generated
indexes.assertHasIndex("idx_custom"); // explicit set
}

@Test
public void testUnique() throws SQLException {
Indexes indexes = new Indexes("index_types");
indexes.assertIsUnique("idx_key");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.javawebstack.orm.test.shared.models;

import org.javawebstack.orm.Model;
import org.javawebstack.orm.annotation.Column;
import org.javawebstack.orm.annotation.Index;

import java.util.UUID;

@Index({"key", "value"})
@Index(value = {"id", "key"}, id = "idx_custom")
@Index(value = {"key"}, unique = true)
public class IndexType extends Model {
@Column
UUID id;
@Column
String key;
@Column
String value;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.javawebstack.orm.test.shared.verification;

import lombok.Builder;
import lombok.ToString;
import org.javawebstack.orm.test.shared.settings.MySQLConnectionContainer;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

public class Indexes extends MySQLConnectionContainer {
String tableName;
List<IndexInfo> indices = new ArrayList<>();

public Indexes(String tableName) throws SQLException {
this.tableName = tableName;
String query = String.format("SHOW INDEXES FROM %s WHERE `key_name` != 'PRIMARY'", tableName);
ResultSet resultSet = sql().read(query);
while (resultSet.next()) {
indices.add(IndexInfo.builder()
.name(resultSet.getString("key_name"))
.type(resultSet.getString("index_type"))
.unique(resultSet.getInt("non_unique") != 1)
.build()
);
}
}

public void assertHasIndex(String indexName) {
assertTrue(
indices.stream().anyMatch(i -> i.name.equals(indexName)),
String.format("Index %s.%s doesn't exist on table.", tableName, indexName)
);
}

public void assertIsUnique(String indexName) {
IndexInfo indexInfo = indices.stream().filter(i -> i.name.equals(indexName)).findFirst().orElse(null);
assertNotNull(indexInfo);
assertTrue(
indexInfo.unique,
String.format("Index %s.%s is not unique.", tableName, indexInfo)
);
}

@Builder
@ToString
public static class IndexInfo {
String name;
String type;
boolean unique;
}
}