Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.molgenis.data.mysql;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static org.molgenis.data.RepositoryCapability.MANAGABLE;
import static org.molgenis.data.RepositoryCapability.QUERYABLE;
import static org.molgenis.data.RepositoryCapability.WRITABLE;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.sql.DataSource;
import org.elasticsearch.common.collect.Iterators;
import org.molgenis.MolgenisFieldTypes;
import org.molgenis.MolgenisFieldTypes.FieldTypeEnum;
import org.molgenis.data.AttributeMetaData;
import org.molgenis.data.DataConverter;
import org.molgenis.data.DataService;
import org.molgenis.data.Entity;
import org.molgenis.data.EntityMetaData;
import org.molgenis.data.Fetch;
import org.molgenis.data.MolgenisDataException;
import org.molgenis.data.MolgenisReferencedEntityException;
import org.molgenis.data.Query;
import org.molgenis.data.QueryRule;
import org.molgenis.data.Repository;
import org.molgenis.data.RepositoryCapability;
import org.molgenis.data.Sort;
import org.molgenis.data.support.AbstractRepository;
import org.molgenis.data.support.BatchingQueryResult;
import org.molgenis.data.support.DefaultEntityMetaData;
import org.molgenis.data.support.QueryImpl;
import org.molgenis.fieldtypes.FieldType;
import org.molgenis.fieldtypes.MrefField;
import org.molgenis.fieldtypes.StringField;
import org.molgenis.fieldtypes.TextField;
import org.molgenis.fieldtypes.XrefField;
import org.molgenis.model.MolgenisModelException;
import org.molgenis.util.EntityUtils;
import org.molgenis.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
public class MysqlRepository extends AbstractRepository
{
private static final Logger LOG = LoggerFactory.getLogger(MysqlRepository.class);
public static final int BATCH_SIZE = 1000;
private EntityMetaData metaData;
private final JdbcTemplate jdbcTemplate;
private final AsyncJdbcTemplate asyncJdbcTemplate;
private final DataService dataService;
private final MySqlEntityFactory mySqlEntityFactory;
private final DataSource dataSource;
private static final String VARCHAR = "VARCHAR(255)";
/**
* Creates a new MysqlRepository.
*
* @param dataSource
* the datasource to use to execute statements on the Mysql database
* @param asyncJdbcTemplate
* {@link AsyncJdbcTemplate} to use to execute DDL statements in an isolated transaction on the Mysql
* database
*/
public MysqlRepository(DataService dataService, MySqlEntityFactory mySqlEntityFactory, DataSource dataSource,
AsyncJdbcTemplate asyncJdbcTemplate)
{
this.dataService = requireNonNull(dataService);
this.mySqlEntityFactory = requireNonNull(mySqlEntityFactory);
this.dataSource = requireNonNull(dataSource);
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.asyncJdbcTemplate = requireNonNull(asyncJdbcTemplate);
}
public void setMetaData(EntityMetaData metaData)
{
this.metaData = metaData;
}
@Override
public void drop()
{
DataAccessException remembered = null;
for (AttributeMetaData att : getEntityMetaData().getAtomicAttributes())
{
if (att.getDataType() instanceof MrefField)
{
DataAccessException e = tryExecute(
"DROP TABLE IF EXISTS `" + getTableName() + "_" + att.getName() + "`");
remembered = remembered != null ? remembered : e;
}
}
// Deleting entites that are referenced won't work due to failing key constraints
// Find out if the entity is referenced and if it is, report those entities
List>> referencingEntities = EntityUtils
.getReferencingEntityMetaData(getEntityMetaData(), dataService);
List>> nonSelfReferencingEntities = referencingEntities.stream()
.filter(ref -> !getEntityMetaData().getName().equals(referencingEntities.get(0).getA().getName()))
.collect(Collectors.toList());
if (!nonSelfReferencingEntities.isEmpty())
{
List entityNames = Lists.newArrayList();
nonSelfReferencingEntities.forEach(pair -> entityNames.add(pair.getA().getName()));
StringBuilder msg = new StringBuilder("Cannot delete entity '").append(getEntityMetaData().getName())
.append("' because it is referenced by the following entities: ").append(entityNames.toString());
throw new MolgenisReferencedEntityException(msg.toString());
}
else
{
DataAccessException e = tryExecute(getDropSql());
remembered = remembered != null ? remembered : e;
}
if (remembered != null)
{
throw remembered;
}
}
/**
* Tries to execute a piece of SQL.
*
* @param sql
* the SQL to execute
* @return Exception if one was caught, or null if all went well
*/
private DataAccessException tryExecute(String sql)
{
try
{
asyncJdbcTemplate.execute(sql);
return null;
}
catch (DataAccessException caught)
{
return caught;
}
}
public void dropAttribute(String attributeName)
{
String sql = String.format("ALTER TABLE `%s` DROP COLUMN `%s`", getTableName(), attributeName);
asyncJdbcTemplate.execute(sql);
DefaultEntityMetaData demd = new DefaultEntityMetaData(metaData);
demd.removeAttributeMetaData(demd.getAttribute(attributeName));
setMetaData(demd);
}
protected String getDropSql()
{
return "DROP TABLE IF EXISTS `" + getTableName() + "`";
}
@Override
public void create()
{
if (tableExists())
{
LOG.debug("Table for entity {} already exists. Skipping creation", getName());
return;
}
try
{
asyncJdbcTemplate.execute(getCreateSql());
for (AttributeMetaData attr : getEntityMetaData().getAtomicAttributes())
{
if (attr.getExpression() != null)
{
// computed attributes are not persisted
continue;
}
// add mref tables
if (attr.getDataType() instanceof MrefField)
{
asyncJdbcTemplate.execute(getMrefCreateSql(attr));
}
else if (attr.getDataType() instanceof XrefField)
{
String backend = dataService.getMeta().getBackend(attr.getRefEntity()).getName();
if (backend.equalsIgnoreCase(MysqlRepositoryCollection.NAME))
{
asyncJdbcTemplate.execute(getCreateFKeySql(attr));
}
}
// text can't be unique, so don't add unique constraint when type is string
if (attr.isUnique() && !(attr.getDataType() instanceof StringField))
{
asyncJdbcTemplate.execute(getUniqueSql(attr));
}
}
}
catch (Exception e)
{
LOG.error("Exception creating MysqlRepository.", e);
try
{
drop();
}
catch (Exception ignored)
{
}
throw new MolgenisDataException(e);
}
}
/**
* Adds an attribute to the repository. Will execute the alter table statement in a different thread so that the
* current transaction does not get committed.
*
* This is needed for adding columns during an import.
*
* @param attributeMetaData
* the {@link AttributeMetaData} to add
*/
protected void addAttribute(AttributeMetaData attributeMetaData)
{
addAttributeInternal(attributeMetaData, true, true);
}
/**
* Adds an attribute to the repository. Will excecute the alter table statement in the current thread. Please note
* that this *will* commit any existing transactions.
*
* This is needed for adding columns in the annotator.
*
* @param attributeMetaData
* the {@link AttributeMetaData} to add
*/
protected void addAttributeSync(AttributeMetaData attributeMetaData)
{
addAttributeInternal(attributeMetaData, true, false);
}
/**
* Adds an attribute to the repository.
*
* @param attributeMetaData
* the {@link AttributeMetaData} to add
* @param addToEntityMetaData
* boolean indicating if the repository's {@link EntityMetaData} should be updated as well. This should
* not happen for parts of a compound attribute.
* @param async
* boolean indicating if the alter table statement should be executed in a different thread or not.
*/
private void addAttributeInternal(AttributeMetaData attributeMetaData, boolean addToEntityMetaData, boolean async)
{
try
{
if (attributeMetaData.getExpression() != null)
{
// computed attributes are not persisted
return;
}
if (attributeMetaData.getDataType() instanceof MrefField)
{
execute(getMrefCreateSql(attributeMetaData), async);
}
else if (!attributeMetaData.getDataType().getEnumType().equals(MolgenisFieldTypes.FieldTypeEnum.COMPOUND))
{
execute(getAlterSql(attributeMetaData), async);
}
if (attributeMetaData.getDataType() instanceof XrefField)
{
execute(getCreateFKeySql(attributeMetaData), async);
}
// TEXT cannot be UNIQUE, don't add constraint when field type is string
if (attributeMetaData.isUnique() && !(attributeMetaData.getDataType() instanceof StringField))
{
execute(getUniqueSql(attributeMetaData), async);
}
if (attributeMetaData.getDataType().getEnumType().equals(MolgenisFieldTypes.FieldTypeEnum.COMPOUND))
{
for (AttributeMetaData attrPart : attributeMetaData.getAttributeParts())
{
addAttributeInternal(attrPart, false, async);
}
}
DefaultEntityMetaData demd = new DefaultEntityMetaData(metaData);
if (addToEntityMetaData)
{
demd.addAttributeMetaData(attributeMetaData);
}
setMetaData(demd);
}
catch (Exception e)
{
LOG.error("Exception updating MysqlRepository.", e);
throw new MolgenisDataException(e);
}
}
/**
* Executes a SQL string.
*
* @param sql
* the String to execute
* @param async
* indication if the string should be executed on a different thread or not
*/
private void execute(String sql, boolean async)
{
if (async)
{
asyncJdbcTemplate.execute(sql);
}
else
{
jdbcTemplate.execute(sql);
}
}
protected String getMrefCreateSql(AttributeMetaData att) throws MolgenisModelException
{
// FIXME Temporary fix for #4623 - remove when switching to generated package/entity/attribut names
String foreignKeyConstraint = getTableName() + "_" + att.getName();
if ((foreignKeyConstraint.length() + "_ibfk_x".length()) > 63)
{
throw new MolgenisModelException("The combination of entity and attribute name [" + foreignKeyConstraint
+ "] is too long to be used as a foreign key constraint by MySQL. Please make sure the combined names are no longer than 56 characters by choosing shorter names.");
}
AttributeMetaData idAttribute = getEntityMetaData().getIdAttribute();
StringBuilder sql = new StringBuilder();
// mysql keys cannot have TEXT value, so change it to VARCHAR when needed
String idAttrMysqlType = (idAttribute.getDataType() instanceof StringField ? VARCHAR
: idAttribute.getDataType().getMysqlType());
String refAttrMysqlType = (att.getRefEntity().getIdAttribute().getDataType() instanceof StringField ? VARCHAR
: att.getRefEntity().getIdAttribute().getDataType().getMysqlType());
sql.append(" CREATE TABLE ").append('`').append(getTableName()).append('_').append(att.getName()).append('`')
.append("(`order` INT,`").append(idAttribute.getName()).append('`').append(' ').append(idAttrMysqlType)
.append(" NOT NULL, ").append('`').append(att.getName()).append('`').append(' ')
.append(refAttrMysqlType).append(" NOT NULL, FOREIGN KEY (").append('`').append(idAttribute.getName())
.append('`').append(") REFERENCES ").append('`').append(getTableName()).append('`').append('(')
.append('`').append(idAttribute.getName()).append("`) ON DELETE CASCADE");
// If the refEntity is not of type MySQL do not add a foreign key to it
String refEntityBackend = dataService.getMeta().getBackend(att.getRefEntity()).getName();
if (refEntityBackend.equalsIgnoreCase(MysqlRepositoryCollection.NAME))
{
sql.append(", FOREIGN KEY (").append('`').append(att.getName()).append('`').append(") REFERENCES ")
.append('`').append(getTableName(att.getRefEntity())).append('`').append('(').append('`')
.append(att.getRefEntity().getIdAttribute().getName()).append("`) ON DELETE CASCADE");
}
sql.append(") ENGINE=InnoDB;");
return sql.toString();
}
protected String getCreateSql() throws MolgenisModelException
{
StringBuilder sql = new StringBuilder();
sql.append("CREATE TABLE IF NOT EXISTS ").append('`').append(getTableName()).append('`').append('(');
for (AttributeMetaData att : getEntityMetaData().getAtomicAttributes())
{
getAttributeSql(sql, att);
if (att.getExpression() == null && !(att.getDataType() instanceof MrefField))
{
sql.append(", ");
}
}
// primary key is first attribute unless otherwise indicated
AttributeMetaData idAttribute = getEntityMetaData().getIdAttribute();
if (idAttribute == null) throw new MolgenisDataException("Missing idAttribute for entity [" + getName() + "]");
if (idAttribute.getDataType() instanceof XrefField || idAttribute.getDataType() instanceof MrefField)
throw new RuntimeException(
"primary key(" + getTableName() + "." + idAttribute.getName() + ") cannot be XREF or MREF");
if (idAttribute.isNillable() == true) throw new RuntimeException(
"idAttribute (" + getTableName() + "." + idAttribute.getName() + ") should not be nillable");
sql.append("PRIMARY KEY (").append('`').append(getEntityMetaData().getIdAttribute().getName()).append('`')
.append(')');
// close
sql.append(") ENGINE=InnoDB;");
if (LOG.isTraceEnabled())
{
LOG.trace("sql: " + sql);
}
return sql.toString();
}
private void getAttributeSql(StringBuilder sql, AttributeMetaData att) throws MolgenisModelException
{
if (att.getExpression() != null)
{
return;
}
switch (att.getDataType().getEnumType())
{
case BOOL:
break;
case DATE:
break;
case DATE_TIME:
break;
case DECIMAL:
break;
case EMAIL:
break;
case ENUM:
break;
case HTML:
break;
case HYPERLINK:
break;
case INT:
break;
case LONG:
break;
case SCRIPT:
break;
case STRING:
break;
case TEXT:
break;
case COMPOUND:
break;
case MREF:
case CATEGORICAL:
case CATEGORICAL_MREF:
case XREF:
case FILE:
if (att.equals(getEntityMetaData().getLabelAttribute()))
{
throw new MolgenisDataException("Attribute [" + att.getName() + "] of entity [" + getName()
+ "] is label attribute and of type [" + att.getDataType()
+ "]. Label attributes cannot be of type xref, mref, categorical or compound.");
}
if (getEntityMetaData().getLookupAttribute(att.getName()) != null)
{
throw new MolgenisDataException("Attribute [" + att.getName() + "] of entity [" + getName()
+ "] is lookup attribute and of type [" + att.getDataType()
+ "]. Lookup attributes cannot be of type xref, mref, categorical or compound.");
}
break;
default:
throw new RuntimeException("Unknown datatype [" + att.getDataType().getEnumType() + "]");
}
if (!(att.getDataType() instanceof MrefField))
{
sql.append('`').append(att.getName()).append('`').append(' ');
// xref adopt type of the identifier of referenced entity
if (att.getDataType() instanceof XrefField)
{
// mysql keys can not be of type TEXT, so don't adopt the field type of a referenced entity when it is
// of fieldtype STRING
if (att.getRefEntity().getIdAttribute().getDataType() instanceof StringField)
{
sql.append(VARCHAR);
}
else
{
sql.append(att.getRefEntity().getIdAttribute().getDataType().getMysqlType());
}
}
else
{
if (att.equals(getEntityMetaData().getIdAttribute()) && att.getDataType() instanceof StringField)
{
// id attributes can not be of type TEXT so we'll change it to VARCHAR
sql.append(VARCHAR);
}
else if (att.isUnique() && att.getDataType() instanceof StringField)
{
// mysql TEXT fields cannot be UNIQUE, so use VARCHAR instead
sql.append(VARCHAR);
}
else
{
sql.append(att.getDataType().getMysqlType());
}
}
// not null
if (!att.isNillable() && !EntityUtils.doesExtend(metaData, "Questionnaire")
&& (att.getVisibleExpression() == null))
{
sql.append(" NOT NULL");
}
// int + auto = auto_increment
if (att.getDataType().equals(MolgenisFieldTypes.INT) && att.isAuto())
{
sql.append(" AUTO_INCREMENT");
}
}
}
public String getAlterSql(AttributeMetaData attributeMetaData) throws MolgenisModelException
{
StringBuilder sql = new StringBuilder();
sql.append("ALTER TABLE ").append('`').append(getTableName()).append('`').append(" ADD ");
getAttributeSql(sql, attributeMetaData);
sql.append(";");
return sql.toString();
}
protected String getCreateFKeySql(AttributeMetaData att)
{
return new StringBuilder().append("ALTER TABLE ").append('`').append(getTableName()).append('`')
.append(" ADD FOREIGN KEY (").append('`').append(att.getName()).append('`').append(") REFERENCES ")
.append('`').append(getTableName(att.getRefEntity())).append('`').append('(').append('`')
.append(att.getRefEntity().getIdAttribute().getName()).append('`').append(")").toString();
}
protected String getUniqueSql(AttributeMetaData att)
{
return new StringBuilder().append("ALTER TABLE ").append('`').append(getTableName()).append('`')
.append(" ADD CONSTRAINT ").append('`').append(att.getName()).append("_unique").append('`')
.append(" UNIQUE (").append('`').append(att.getName()).append('`').append(")").toString();
}
@Override
public EntityMetaData getEntityMetaData()
{
return metaData;
}
@Override
public Iterator iterator()
{
Query q = new QueryImpl();
return findAllBatching(q).iterator();
}
@Override
public Stream stream(Fetch fetch)
{
Query q = new QueryImpl();
if (fetch != null)
{
q.fetch(fetch);
}
return StreamSupport.stream(findAllBatching(q).spliterator(), false);
}
protected String getInsertSql()
{
StringBuilder sql = new StringBuilder();
sql.append("INSERT INTO ").append('`').append(getTableName()).append('`').append(" (");
StringBuilder params = new StringBuilder();
for (AttributeMetaData att : getEntityMetaData().getAtomicAttributes())
{
if (att.getExpression() != null)
{
continue;
}
if (!(att.getDataType() instanceof MrefField))
{
sql.append('`').append(att.getName()).append('`').append(", ");
params.append("?, ");
}
}
if (sql.charAt(sql.length() - 1) == ' ' && sql.charAt(sql.length() - 2) == ',')
{
sql.setLength(sql.length() - 2);
params.setLength(params.length() - 2);
}
sql.append(") VALUES (").append(params).append(")");
return sql.toString();
}
private void removeMrefs(final List