@ -18,11 +18,11 @@ package org.thingsboard.server.dao.sql.relation;
import com.google.common.util.concurrent.ListenableFuture ;
import lombok.extern.slf4j.Slf4j ;
import org.springframework.beans.factory.annotation.Autowired ;
import org.springframework.dao.ConcurrencyFailureException ;
import org.springframework.dao.DataAccessException ;
import org.springframework.data.domain.PageRequest ;
import org.springframework.stereotype.Component ;
import org.springframework.util.CollectionUtils ;
import org.thingsboard.server.common.data.id.EntityId ;
import org.thingsboard.server.common.data.id.EntityIdFactory ;
import org.thingsboard.server.common.data.id.TenantId ;
import org.thingsboard.server.common.data.relation.EntityRelation ;
import org.thingsboard.server.common.data.relation.RelationTypeGroup ;
@ -36,11 +36,19 @@ import org.thingsboard.server.dao.util.SqlDao;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Collection ;
import java.util.Collections ;
import java.util.List ;
import java.util.UUID ;
import java.util.stream.Collectors ;
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_FROM_ID_PROPERTY ;
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_FROM_TYPE_PROPERTY ;
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TO_ID_PROPERTY ;
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TO_TYPE_PROPERTY ;
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TYPE_GROUP_PROPERTY ;
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TYPE_PROPERTY ;
import static org.thingsboard.server.dao.model.ModelConstants.VERSION_COLUMN ;
/ * *
* Created by Valerii Sosliuk on 5 / 29 / 2017 .
* /
@ -50,6 +58,8 @@ import java.util.stream.Collectors;
public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService implements RelationDao {
private static final List < String > ALL_TYPE_GROUP_NAMES = new ArrayList < > ( ) ;
private static final String RETURNING = "RETURNING from_id, from_type, to_id, to_type, relation_type, relation_type_group, nextval('relation_version_seq') as version" ;
private static final String DELETE_QUERY = "DELETE FROM relation WHERE from_id = ? AND from_type = ? AND to_id = ? AND to_type = ? AND relation_type = ? AND relation_type_group = ? " + RETURNING ;
static {
Arrays . stream ( RelationTypeGroup . values ( ) ) . map ( RelationTypeGroup : : name ) . forEach ( ALL_TYPE_GROUP_NAMES : : add ) ;
@ -144,107 +154,138 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
}
@Override
public boolea n saveRelation ( TenantId tenantId , EntityRelation relation ) {
return relationInsertRepository . saveOrUpdate ( new RelationEntity ( relation ) ) ! = null ;
public EntityRelatio n saveRelation ( TenantId tenantId , EntityRelation relation ) {
return DaoUtil . getData ( relationInsertRepository . saveOrUpdate ( new RelationEntity ( relation ) ) ) ;
}
@Override
public void saveRelations ( TenantId tenantId , Collection < EntityRelation > relations ) {
public List < EntityRelation > saveRelations ( TenantId tenantId , List < EntityRelation > relations ) {
List < RelationEntity > entities = relations . stream ( ) . map ( RelationEntity : : new ) . collect ( Collectors . toList ( ) ) ;
relationInsertRepository . saveOrUpdate ( entities ) ;
return DaoUtil . convertDataList ( relationInsertRepository . saveOrUpdate ( entities ) ) ;
}
@Override
public ListenableFuture < Boolea n> saveRelationAsync ( TenantId tenantId , EntityRelation relation ) {
return service . submit ( ( ) - > relationInsertRepository . saveOrUpdate ( new RelationEntity ( relation ) ) ! = null ) ;
public ListenableFuture < EntityRelatio n> saveRelationAsync ( TenantId tenantId , EntityRelation relation ) {
return service . submit ( ( ) - > DaoUtil . getData ( relationInsertRepository . saveOrUpdate ( new RelationEntity ( relation ) ) ) ) ;
}
@Override
public boolea n deleteRelation ( TenantId tenantId , EntityRelation relation ) {
public EntityRelatio n deleteRelation ( TenantId tenantId , EntityRelation relation ) {
RelationCompositeKey key = new RelationCompositeKey ( relation ) ;
return deleteRelationIfExists ( key ) ;
}
@Override
public ListenableFuture < Boolea n> deleteRelationAsync ( TenantId tenantId , EntityRelation relation ) {
public ListenableFuture < EntityRelatio n> deleteRelationAsync ( TenantId tenantId , EntityRelation relation ) {
RelationCompositeKey key = new RelationCompositeKey ( relation ) ;
return service . submit (
( ) - > deleteRelationIfExists ( key ) ) ;
}
@Override
public boolea n deleteRelation ( TenantId tenantId , EntityId from , EntityId to , String relationType , RelationTypeGroup typeGroup ) {
public EntityRelatio n deleteRelation ( TenantId tenantId , EntityId from , EntityId to , String relationType , RelationTypeGroup typeGroup ) {
RelationCompositeKey key = getRelationCompositeKey ( from , to , relationType , typeGroup ) ;
return deleteRelationIfExists ( key ) ;
}
@Override
public ListenableFuture < Boolea n> deleteRelationAsync ( TenantId tenantId , EntityId from , EntityId to , String relationType , RelationTypeGroup typeGroup ) {
public ListenableFuture < EntityRelatio n> deleteRelationAsync ( TenantId tenantId , EntityId from , EntityId to , String relationType , RelationTypeGroup typeGroup ) {
RelationCompositeKey key = getRelationCompositeKey ( from , to , relationType , typeGroup ) ;
return service . submit (
( ) - > deleteRelationIfExists ( key ) ) ;
}
private boolean deleteRelationIfExists ( RelationCompositeKey key ) {
boolean relationExistsBeforeDelete = relationRepository . existsById ( key ) ;
if ( relationExistsBeforeDelete ) {
try {
relationRepository . deleteById ( key ) ;
} catch ( DataAccessException e ) {
log . debug ( "[{}] Concurrency exception while deleting relation" , key , e ) ;
private EntityRelation deleteRelationIfExists ( RelationCompositeKey key ) {
return jdbcTemplate . query ( DELETE_QUERY , rs - > {
if ( ! rs . next ( ) ) {
return null ;
}
}
return relationExistsBeforeDelete ;
EntityRelation relation = new EntityRelation ( ) ;
var fromId = rs . getObject ( RELATION_FROM_ID_PROPERTY , UUID . class ) ;
var fromType = rs . getString ( RELATION_FROM_TYPE_PROPERTY ) ;
var toId = rs . getObject ( RELATION_TO_ID_PROPERTY , UUID . class ) ;
var toType = rs . getString ( RELATION_TO_TYPE_PROPERTY ) ;
var relationTypeGroup = rs . getString ( RELATION_TYPE_GROUP_PROPERTY ) ;
var relationType = rs . getString ( RELATION_TYPE_PROPERTY ) ;
var version = rs . getLong ( VERSION_COLUMN ) ;
//additionalInfo ignored (no need to send extra data for delete events)
relation . setTo ( EntityIdFactory . getByTypeAndUuid ( toType , toId ) ) ;
relation . setFrom ( EntityIdFactory . getByTypeAndUuid ( fromType , fromId ) ) ;
relation . setType ( relationType ) ;
relation . setTypeGroup ( RelationTypeGroup . valueOf ( relationTypeGroup ) ) ;
relation . setVersion ( version ) ;
return relation ;
} , key . getFromId ( ) , key . getFromType ( ) , key . getToId ( ) , key . getToType ( ) , key . getRelationType ( ) , key . getRelationTypeGroup ( ) ) ;
}
@Override
public void deleteOutboundRelations ( TenantId tenantId , EntityId entity ) {
try {
relationRepository . deleteByFromIdAndFromType ( entity . getId ( ) , entity . getEntityType ( ) . name ( ) ) ;
} catch ( ConcurrencyFailureException e ) {
log . debug ( "Concurrency exception while deleting relations [{}]" , entity , e ) ;
}
public List < EntityRelation > deleteOutboundRelations ( TenantId tenantId , EntityId entity ) {
return deleteRelations ( entity , null , false ) ;
}
@Override
public void deleteOutboundRelations ( TenantId tenantId , EntityId entity , RelationTypeGroup relationTypeGroup ) {
try {
relationRepository . deleteByFromIdAndFromTypeAndRelationTypeGroupIn ( entity . getId ( ) , entity . getEntityType ( ) . name ( ) , Collections . singletonList ( relationTypeGroup . name ( ) ) ) ;
} catch ( ConcurrencyFailureException e ) {
log . debug ( "Concurrency exception while deleting relations [{}]" , entity , e ) ;
}
public List < EntityRelation > deleteOutboundRelations ( TenantId tenantId , EntityId entity , RelationTypeGroup relationTypeGroup ) {
return deleteRelations ( entity , Collections . singletonList ( relationTypeGroup . name ( ) ) , false ) ;
}
@Override
public void deleteInboundRelations ( TenantId tenantId , EntityId entity ) {
try {
relationRepository . deleteByToIdAndToTypeAndRelationTypeGroupIn ( entity . getId ( ) , entity . getEntityType ( ) . name ( ) , ALL_TYPE_GROUP_NAMES ) ;
} catch ( ConcurrencyFailureException e ) {
log . debug ( "Concurrency exception while deleting relations [{}]" , entity , e ) ;
}
public List < EntityRelation > deleteInboundRelations ( TenantId tenantId , EntityId entity ) {
return deleteRelations ( entity , ALL_TYPE_GROUP_NAMES , true ) ;
}
@Override
public void deleteInboundRelations ( TenantId tenantId , EntityId entity , RelationTypeGroup relationTypeGroup ) {
try {
relationRepository . deleteByToIdAndToTypeAndRelationTypeGroupIn ( entity . getId ( ) , entity . getEntityType ( ) . name ( ) , Collections . singletonList ( relationTypeGroup . name ( ) ) ) ;
} catch ( ConcurrencyFailureException e ) {
log . debug ( "Concurrency exception while deleting relations [{}]" , entity , e ) ;
}
public List < EntityRelation > deleteInboundRelations ( TenantId tenantId , EntityId entity , RelationTypeGroup relationTypeGroup ) {
return deleteRelations ( entity , Collections . singletonList ( relationTypeGroup . name ( ) ) , true ) ;
}
@Override
public ListenableFuture < Boolean > deleteOutboundRelationsAsync ( TenantId tenantId , EntityId entity ) {
return service . submit (
( ) - > {
boolean relationExistsBeforeDelete = relationRepository
. findAllByFromIdAndFromType ( entity . getId ( ) , entity . getEntityType ( ) . name ( ) )
. size ( ) > 0 ;
if ( relationExistsBeforeDelete ) {
relationRepository . deleteByFromIdAndFromType ( entity . getId ( ) , entity . getEntityType ( ) . name ( ) ) ;
}
return relationExistsBeforeDelete ;
} ) ;
private List < EntityRelation > deleteRelations ( EntityId entityId , List < String > relationTypeGroups , boolean inbound ) {
List < Object > params = new ArrayList < > ( ) ;
params . add ( entityId . getId ( ) ) ;
params . add ( entityId . getEntityType ( ) . name ( ) ) ;
StringBuilder sqlBuilder = new StringBuilder ( "DELETE FROM relation WHERE " ) ;
if ( inbound ) {
sqlBuilder . append ( "to_id = ? AND to_type = ? " ) ;
} else {
sqlBuilder . append ( "from_id = ? AND from_type = ? " ) ;
}
if ( ! CollectionUtils . isEmpty ( relationTypeGroups ) ) {
sqlBuilder . append ( "AND relation_type_group IN (?" ) ;
for ( int i = 1 ; i < relationTypeGroups . size ( ) ; i + + ) {
sqlBuilder . append ( ", ?" ) ;
}
sqlBuilder . append ( ")" ) ;
params . addAll ( relationTypeGroups ) ;
}
sqlBuilder . append ( RETURNING ) ;
return jdbcTemplate . queryForList ( sqlBuilder . toString ( ) , params . toArray ( ) ) . stream ( )
. map ( row - > {
EntityRelation relation = new EntityRelation ( ) ;
var fromId = row . get ( RELATION_FROM_ID_PROPERTY ) ;
var fromType = row . get ( RELATION_FROM_TYPE_PROPERTY ) ;
var toId = row . get ( RELATION_TO_ID_PROPERTY ) ;
var toType = row . get ( RELATION_TO_TYPE_PROPERTY ) ;
var relationTypeGroup = row . get ( RELATION_TYPE_GROUP_PROPERTY ) ;
var relationType = row . get ( RELATION_TYPE_PROPERTY ) ;
var version = row . get ( VERSION_COLUMN ) ;
//additionalInfo ignored (no need to send extra data for delete events)
relation . setTo ( EntityIdFactory . getByTypeAndUuid ( ( String ) toType , ( UUID ) toId ) ) ;
relation . setFrom ( EntityIdFactory . getByTypeAndUuid ( ( String ) fromType , ( UUID ) fromId ) ) ;
relation . setType ( ( String ) relationType ) ;
relation . setTypeGroup ( RelationTypeGroup . valueOf ( ( String ) relationTypeGroup ) ) ;
relation . setVersion ( ( Long ) version ) ;
return relation ;
} )
. collect ( Collectors . toList ( ) ) ;
}
@Override