blob: f34d92056eed9cbb896efff592d42fbb4f982c87 [file] [log] [blame]
/*
* 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.polygene.entitystore.sql;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.apache.polygene.api.composite.TransientBuilderFactory;
import org.apache.polygene.api.configuration.Configuration;
import org.apache.polygene.api.entity.EntityDescriptor;
import org.apache.polygene.api.entity.EntityReference;
import org.apache.polygene.api.injection.scope.Service;
import org.apache.polygene.api.injection.scope.Structure;
import org.apache.polygene.api.injection.scope.This;
import org.apache.polygene.api.injection.scope.Uses;
import org.apache.polygene.api.mixin.Mixins;
import org.apache.polygene.api.serialization.Serialization;
import org.apache.polygene.api.service.ServiceActivation;
import org.apache.polygene.api.service.ServiceDescriptor;
import org.apache.polygene.api.structure.Application;
import org.apache.polygene.api.structure.ModuleDescriptor;
import org.apache.polygene.spi.entitystore.EntityStoreUnitOfWork;
import org.apache.polygene.spi.entitystore.helpers.DefaultEntityState;
import org.jooq.Record;
import org.jooq.SQLDialect;
import org.jooq.SelectQuery;
import org.jooq.conf.Settings;
/**
* This class handles all the Jooq interactions.
* <p>
* <p>
* <p>
* <h1>Tables</h1>
* <h2>Types Table</h2>
* <ul>
* <li>identity</li>
* <li>table_name</li>
* <li>created_at</li>
* <li>modified_at</li>
* </ul>
* <h2>Entities Table</h2>
* <ul>
* <li>identity</li>
* <li>app_version</li>
* <li>value_id</li>
* <li>version</li>
* <li>type</li>
* <li>modified_at</li>
* <li>created_at</li>
* </ul>
* <h2>Mixin Tables</h2>
* <p>
* Each Mixin is stored in its own table. Only the following column is always present;
* </p>
* <ul>
* <li>identity - this is not entity identity but the UUID of the value_id in the Entities Table above.</li>
* </ul>
* <p>
* Each Property of the Mixin (as defined by QualifiedName of the Property, will reside in its own column.
* All values in columns are (for now) serialized using a ValueSerialization service.
* </p>
* <p>
* Associations also has their own columns in the table, with the EntityReference.identity() stored in them.
* </p>
* <p>
* ManyAssociations and NamedAssociations are stored in a separate table, named &lt;mixintable&gt;_ASSOCS, see below.
* </p>
* <h2>Mixin_ASSOCS Table</h2>
* <ul>
* <li>identity - the value_id of the mixin value</li>
* <li>name - the name of the ManyAssociation or NamedAssociation</li>
* <li>position - for NamedAssociation this is the 'name' (i.e key) and for ManyAssociation this is the index into the list.</li>
* <li>reference - EntityReference.identity of that association</li>
* </ul>
*/
@Mixins( SqlTable.Mixin.class )
public interface SqlTable extends ServiceActivation
{
BaseEntity fetchBaseEntity( EntityReference reference, ModuleDescriptor module );
SelectQuery<Record> createGetEntityQuery( EntityDescriptor descriptor, EntityReference reference );
void fetchAssociations( BaseEntity entity, EntityDescriptor descriptor, Consumer<AssociationValue> consume );
BaseEntity createNewBaseEntity( EntityReference ref, EntityDescriptor descriptor, EntityStoreUnitOfWork unitOfWork );
void insertEntity( DefaultEntityState state, BaseEntity baseEntity, EntityStoreUnitOfWork unitOfWork );
void updateEntity( DefaultEntityState state, BaseEntity baseEntity, EntityStoreUnitOfWork unitOfWork );
JooqDslContext jooqDslContext();
void removeEntity( EntityReference entityReference, EntityDescriptor descriptor );
Stream<BaseEntity> fetchAll( EntityDescriptor type, ModuleDescriptor module );
class Mixin
implements SqlTable, TableFields, ServiceActivation
{
@Structure
private Application application;
@Structure
private TransientBuilderFactory tbf;
@This
private Configuration<SqlEntityStoreConfiguration> configuration;
@Service
private Serialization serialization;
@Uses
private ServiceDescriptor serviceDescriptor;
private EntitiesTable entitiesTable;
private TypesTable types;
private JooqDslContext dsl;
@Override
public BaseEntity fetchBaseEntity( EntityReference reference, ModuleDescriptor module )
{
return entitiesTable.fetchEntity( reference, module );
}
@Override
public Stream<BaseEntity> fetchAll( EntityDescriptor type, ModuleDescriptor module )
{
return entitiesTable.fetchAll( type, module );
}
@Override
public SelectQuery<Record> createGetEntityQuery( EntityDescriptor descriptor, EntityReference reference )
{
return entitiesTable.createGetEntityQuery( descriptor, reference );
}
@Override
public void fetchAssociations( BaseEntity entity, EntityDescriptor descriptor, Consumer<AssociationValue> consume )
{
entitiesTable.fetchAssociations( entity, descriptor, consume );
}
@Override
public BaseEntity createNewBaseEntity( EntityReference ref, EntityDescriptor descriptor, EntityStoreUnitOfWork unitOfWork )
{
return entitiesTable.createNewBaseEntity( ref, descriptor, unitOfWork );
}
@Override
public void insertEntity( DefaultEntityState state, BaseEntity baseEntity, EntityStoreUnitOfWork unitOfWork )
{
entitiesTable.insertEntity( state, baseEntity );
}
@Override
public void updateEntity( DefaultEntityState state, BaseEntity baseEntity, EntityStoreUnitOfWork unitOfWork )
{
entitiesTable.modifyEntity( state, baseEntity, unitOfWork );
}
@Override
public JooqDslContext jooqDslContext()
{
return dsl;
}
@Override
public void removeEntity( EntityReference reference, EntityDescriptor descriptor )
{
entitiesTable.removeEntity( reference, descriptor );
}
@Override
public void activateService()
{
SqlEntityStoreConfiguration config = this.configuration.get();
SQLDialect dialect = getSqlDialect( config );
Settings settings = serviceDescriptor.metaInfo( Settings.class );
String typesTableName = config.typesTableName().get();
String entitiesTableName = config.entitiesTableName().get();
dsl = tbf.newTransient( JooqDslContext.class, settings, dialect );
types = new TypesTable( dsl, dialect, typesTableName, config );
entitiesTable = new EntitiesTable( dsl, dialect, types, application.version(), entitiesTableName, serialization );
if( config.createIfMissing().get() )
{
dsl.transaction( t -> {
types.create();
entitiesTable.create();
} );
}
}
@Override
public void passivateService()
{
}
private SQLDialect getSqlDialect( SqlEntityStoreConfiguration config )
{
SQLDialect dialect;
String dialectString = config.dialect().get();
if( dialectString.length() == 0 )
{
dialect = SQLDialect.DEFAULT;
}
else
{
try
{
dialect = SQLDialect.valueOf( dialectString );
}
catch( IllegalArgumentException e )
{
throw new IllegalArgumentException( "Invalid SQLDialect: '" + dialectString + "'" );
}
}
return dialect;
}
}
}