Commit 87cf4b1b authored by PECQUOT's avatar PECQUOT
Browse files

[fix] TripServiceImpl.saveParent: save landing without lock

[enh] add MeasurementDao.deleteMeasurements
[fix] add some @ConditionalOnBean(ExtractionConfiguration.class) when sumaris.extraction.enabled=false
parent 8c5a054d
Subproject commit fe643f48f8687957d8b20871f95a36e19fedb5b4
Subproject commit 16aacd4f618a615e3e1121bff3cc21cc68971987
......@@ -34,6 +34,7 @@ import net.sumaris.core.dao.technical.SortDirection;
import net.sumaris.core.exception.DataNotFoundException;
import net.sumaris.core.exception.SumarisTechnicalException;
import net.sumaris.core.extraction.config.ExtractionCacheConfiguration;
import net.sumaris.core.extraction.config.ExtractionConfiguration;
import net.sumaris.core.extraction.dao.AggregationDao;
import net.sumaris.core.extraction.dao.technical.Daos;
import net.sumaris.core.extraction.dao.technical.table.ExtractionTableColumnOrder;
......@@ -58,6 +59,7 @@ import org.apache.commons.lang3.ArrayUtils;
import org.nuiton.i18n.I18n;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
......@@ -77,6 +79,7 @@ import java.util.stream.Collectors;
* @author peck7 on 17/12/2018.
*/
@Service("aggregationService")
@ConditionalOnBean(ExtractionConfiguration.class)
@Slf4j
public class AggregationServiceImpl implements AggregationService {
......
......@@ -27,10 +27,11 @@ import com.google.common.base.Joiner;
import lombok.NonNull;
import net.sumaris.core.config.SumarisConfiguration;
import net.sumaris.core.exception.SumarisTechnicalException;
import net.sumaris.core.extraction.config.ExtractionConfiguration;
import net.sumaris.core.extraction.dao.technical.table.ExtractionTableColumnOrder;
import net.sumaris.core.model.technical.extraction.IExtractionFormat;
import net.sumaris.core.model.technical.extraction.ExtractionCategoryEnum;
import net.sumaris.core.extraction.vo.ExtractionTypeVO;
import net.sumaris.core.model.technical.extraction.ExtractionCategoryEnum;
import net.sumaris.core.model.technical.extraction.IExtractionFormat;
import net.sumaris.core.util.Beans;
import net.sumaris.core.util.Files;
import net.sumaris.core.util.ResourceUtils;
......@@ -42,6 +43,7 @@ import org.nuiton.i18n.I18n;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
......@@ -54,6 +56,7 @@ import java.util.Locale;
import java.util.Optional;
@Component("extractionDocumentationService")
@ConditionalOnBean(ExtractionConfiguration.class)
public class ExtractionDocumentationServiceImpl implements ExtractionDocumentationService {
/** Logger. */
......
......@@ -23,6 +23,7 @@ package net.sumaris.core.extraction.service;
*/
import net.sumaris.core.dao.technical.extraction.ExtractionProductRepository;
import net.sumaris.core.extraction.config.ExtractionConfiguration;
import net.sumaris.core.extraction.dao.technical.table.ExtractionTableColumnOrder;
import net.sumaris.core.extraction.dao.technical.table.ExtractionTableDao;
import net.sumaris.core.util.StringUtils;
......@@ -31,15 +32,18 @@ import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.List;
import java.util.Optional;
/**
* @author benoit.lavenier@e-is.pro
*/
@Service("extractionProductService")
@ConditionalOnBean(ExtractionConfiguration.class)
public class ExtractionProductServiceImpl implements ExtractionProductService {
private static final Logger log = LoggerFactory.getLogger(ExtractionProductServiceImpl.class);
......
......@@ -80,6 +80,7 @@ import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.mutable.MutableInt;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.core.task.TaskExecutor;
......@@ -100,6 +101,7 @@ import java.util.stream.Collectors;
* @author peck7 on 17/12/2018.
*/
@Service("extractionService")
@ConditionalOnBean(ExtractionConfiguration.class)
@Slf4j
public class ExtractionServiceImpl implements ExtractionService {
......
......@@ -38,6 +38,7 @@ import net.sumaris.core.vo.technical.extraction.ExtractionProductFilterVO;
import net.sumaris.core.vo.technical.extraction.ExtractionProductVO;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
......@@ -48,6 +49,7 @@ import java.util.List;
import java.util.stream.Collectors;
@Component
@ConditionalOnBean(ExtractionConfiguration.class)
@Slf4j
public class ExtractionJob {
......
......@@ -28,6 +28,7 @@ import net.sumaris.core.event.config.ConfigurationEvent;
import net.sumaris.core.event.config.ConfigurationReadyEvent;
import net.sumaris.core.event.config.ConfigurationUpdatedEvent;
import net.sumaris.core.exception.UnauthorizedException;
import net.sumaris.core.extraction.config.ExtractionConfiguration;
import net.sumaris.core.extraction.service.AggregationService;
import net.sumaris.core.extraction.vo.AggregationTypeVO;
import net.sumaris.core.extraction.vo.ExtractionTypeVO;
......@@ -53,7 +54,7 @@ import java.util.Optional;
* @author benoit.lavenier@e-is.pro
*/
@Service("extractionSecurityService")
@ConditionalOnBean({IAuthService.class})
@ConditionalOnBean({ExtractionConfiguration.class, IAuthService.class})
public class ExtractionSecurityServiceImpl implements ExtractionSecurityService {
private static final Logger log = LoggerFactory.getLogger(ExtractionSecurityServiceImpl.class);
......
......@@ -47,4 +47,6 @@ public interface SumarisJpaRepository<E extends IEntity<ID>, ID extends Serializ
E createEntity();
V save(V vo);
V save(V vo, boolean checkUpdateDate, boolean lockForUpdate);
}
......@@ -39,7 +39,6 @@ import net.sumaris.core.exception.SumarisTechnicalException;
import net.sumaris.core.util.Beans;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.Session;
import org.hibernate.dialect.Dialect;
......@@ -152,7 +151,7 @@ public abstract class SumarisJpaRepositoryImpl<E extends IEntity<ID>, ID extends
@Override
public V save(V vo) {
return save(vo, isCheckUpdateDate());
return save(vo, isCheckUpdateDate(), isLockForUpdate());
}
public E toEntity(V vo) {
......@@ -184,7 +183,7 @@ public abstract class SumarisJpaRepositoryImpl<E extends IEntity<ID>, ID extends
Beans.copyProperties(source, target);
}
protected V save(V vo, boolean checkUpdateDate) {
public V save(V vo, boolean checkUpdateDate, boolean lockForUpdate) {
E entity = toEntity(vo);
boolean isNew = entity.getId() == null;
......@@ -201,7 +200,7 @@ public abstract class SumarisJpaRepositoryImpl<E extends IEntity<ID>, ID extends
((IUpdateDateEntityBean) entity).setUpdateDate(getDatabaseCurrentTimestamp());
}
if (!isNew && isLockForUpdate()) {
if (!isNew && lockForUpdate) {
lockForUpdate(entity);
}
......
......@@ -33,6 +33,8 @@ sumaris.config.option.hibernate.default_schema.description=
sumaris.config.option.i18n.directory.description=
sumaris.config.option.i18n.locale.description=
sumaris.config.option.inceptionYear.description=
sumaris.config.option.javax.persistence.lock.mode.description=
sumaris.config.option.javax.persistence.lock.timeout.description=
sumaris.config.option.launch.mode.description=
sumaris.config.option.liquibase.changelog.path.description=
sumaris.config.option.liquibase.diff.types.description=
......
......@@ -33,6 +33,8 @@ sumaris.config.option.hibernate.default_schema.description=
sumaris.config.option.i18n.directory.description=
sumaris.config.option.i18n.locale.description=
sumaris.config.option.inceptionYear.description=
sumaris.config.option.javax.persistence.lock.mode.description=
sumaris.config.option.javax.persistence.lock.timeout.description=
sumaris.config.option.launch.mode.description=
sumaris.config.option.liquibase.changelog.path.description=
sumaris.config.option.liquibase.diff.types.description=
......
......@@ -10,12 +10,12 @@ package net.sumaris.core.dao.data;
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
......@@ -27,6 +27,7 @@ import net.sumaris.core.model.data.IMeasurementEntity;
import net.sumaris.core.vo.data.MeasurementVO;
import net.sumaris.core.vo.data.QuantificationMeasurementVO;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
......@@ -42,6 +43,11 @@ public interface MeasurementDao {
List<T> target,
IEntity<?> parent);
<ID extends Serializable, T extends IMeasurementEntity> void deleteMeasurements(
final Class<T> targetClass,
final Class<? extends IEntity<ID>> parentClass,
final Collection<ID> parentIds);
<T extends IMeasurementEntity> List<T> getMeasurementEntitiesByParentId(Class<T> entityClass,
String parentPropertyName,
int parentId,
......
......@@ -59,10 +59,7 @@ import org.springframework.stereotype.Repository;
import javax.annotation.Nullable;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.*;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.sql.Timestamp;
......@@ -745,6 +742,38 @@ public class MeasurementDaoImpl extends HibernateDaoSupport implements Measureme
return result;
}
@Override
public <ID extends Serializable, T extends IMeasurementEntity> void deleteMeasurements(Class<T> targetClass, Class<? extends IEntity<ID>> parentClass, Collection<ID> parentIds) {
if (targetClass == null || parentClass == null || CollectionUtils.isEmpty(parentIds))
return;
Collection<PropertyDescriptor> parentDescriptors = parentPropertiesMap.get(targetClass);
if (CollectionUtils.isNotEmpty(parentDescriptors)) {
// Find the right parent property (use the first compatible parent)
PropertyDescriptor parentProperty = parentDescriptors.stream()
.filter(property -> property.getPropertyType().isAssignableFrom(parentClass))
.findFirst().orElse(null);
// If a parent property has been found, use it
if (parentProperty != null) {
// Build delete query
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaDelete<T> criteria = cb.createCriteriaDelete(targetClass);
criteria.from(targetClass);
criteria.where(cb.in(criteria.getRoot().get(parentProperty.getName()).get(IEntity.Fields.ID)).value(parentIds));
int deleted = getEntityManager().createQuery(criteria).executeUpdate();
if (log.isDebugEnabled()) {
log.debug(String.format("%d %s deleted by parent '%s':%s", deleted, targetClass.getSimpleName(), parentProperty.getName(), parentIds));
}
getEntityManager().flush();
getEntityManager().clear();
}
}
}
@Override
public <T extends IMeasurementEntity> List<T> getMeasurementEntitiesByParentId(Class<T> entityClass, String parentPropertyName, int parentId, String sortByPropertyName) {
return getMeasurementsByParentIdQuery(entityClass, parentPropertyName, parentId, sortByPropertyName)
......@@ -1112,7 +1141,7 @@ public class MeasurementDaoImpl extends HibernateDaoSupport implements Measureme
Collection<PropertyDescriptor> parentDescriptors = parentPropertiesMap.get(target.getClass());
if (CollectionUtils.isNotEmpty(parentDescriptors)) {
// Find th right parent property (use the first compatible parent)
// Find the right parent property (use the first compatible parent)
PropertyDescriptor parentProperty = parentDescriptors.stream()
.filter(property -> property.getPropertyType().isAssignableFrom(parentClass))
.findFirst().orElse(null);
......@@ -1134,6 +1163,7 @@ public class MeasurementDaoImpl extends HibernateDaoSupport implements Measureme
}
// No parent property in the global map: continue as a special case
// TODO clean up following cases, should be already managed by parentPropertiesMap
// If vessel use measurement
if (target instanceof VesselUseMeasurement) {
......
......@@ -35,7 +35,10 @@ import net.sumaris.core.model.referential.VesselType;
import net.sumaris.core.util.StringUtils;
import net.sumaris.core.vo.administration.programStrategy.ProgramVO;
import net.sumaris.core.vo.administration.user.DepartmentVO;
import net.sumaris.core.vo.data.*;
import net.sumaris.core.vo.data.DataFetchOptions;
import net.sumaris.core.vo.data.VesselFeaturesVO;
import net.sumaris.core.vo.data.VesselRegistrationPeriodVO;
import net.sumaris.core.vo.data.VesselVO;
import net.sumaris.core.vo.data.vessel.VesselFetchOptions;
import net.sumaris.core.vo.filter.VesselFilterVO;
import net.sumaris.core.vo.referential.ReferentialVO;
......@@ -140,11 +143,6 @@ public class VesselRepositoryImpl
;
}
@Override
public VesselVO save(VesselVO vo, boolean checkUpdateDate) {
return super.save(vo, checkUpdateDate);
}
@Override
public void toVO(Vessel source, VesselVO target, VesselFetchOptions fetchOptions, boolean copyIfNull) {
super.toVO(source, target, fetchOptions, copyIfNull);
......
......@@ -33,7 +33,6 @@ import net.sumaris.core.model.referential.VesselType;
import net.sumaris.core.model.referential.location.Location;
import net.sumaris.core.model.referential.location.LocationHierarchy;
import net.sumaris.core.util.StringUtils;
import net.sumaris.core.vo.data.VesselVO;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.data.jpa.domain.Specification;
......@@ -227,5 +226,4 @@ public interface VesselSpecifications extends RootDataSpecifications<Vessel> {
return specification;
}
VesselVO save(VesselVO vo, boolean checkUpdateDate);
}
......@@ -101,6 +101,8 @@ public enum PmfmEnum implements Serializable {
STRATEGY_LABEL(359, "STRATEGY_LABEL"),
REFUSED_SURVEY(266, "REFUSED_SURVEY")
;
public static PmfmEnum valueOf(final int id) {
......
......@@ -23,15 +23,15 @@ package net.sumaris.core.service.data;
*/
import com.google.common.base.Preconditions;
import com.google.common.collect.*;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import lombok.extern.slf4j.Slf4j;
import net.sumaris.core.dao.administration.programStrategy.ProgramRepository;
import net.sumaris.core.dao.data.MeasurementDao;
import net.sumaris.core.dao.data.landing.LandingRepository;
import net.sumaris.core.dao.data.operation.OperationGroupRepository;
import net.sumaris.core.dao.data.trip.TripRepository;
import net.sumaris.core.dao.referential.metier.MetierRepository;
import net.sumaris.core.dao.technical.Page;
import net.sumaris.core.exception.SumarisTechnicalException;
import net.sumaris.core.service.data.vessel.VesselService;
import net.sumaris.core.util.Beans;
......@@ -69,8 +69,7 @@ public class AggregatedLandingServiceImpl implements AggregatedLandingService {
.build();
private final LandingService landingService;
private final LandingRepository landingRepository;
private final TripRepository tripRepository;
private final TripService tripService;
private final ObservedLocationService observedLocationService;
private final OperationGroupRepository operationGroupRepository;
private final MeasurementDao measurementDao;
......@@ -79,8 +78,7 @@ public class AggregatedLandingServiceImpl implements AggregatedLandingService {
private final ProgramRepository programRepository;
public AggregatedLandingServiceImpl(LandingService landingService,
LandingRepository landingRepository,
TripRepository tripRepository,
TripService tripService,
ObservedLocationService observedLocationService,
OperationGroupRepository operationGroupRepository,
MeasurementDao measurementDao,
......@@ -88,8 +86,7 @@ public class AggregatedLandingServiceImpl implements AggregatedLandingService {
VesselService vesselService,
ProgramRepository programRepository) {
this.landingService = landingService;
this.landingRepository = landingRepository;
this.tripRepository = tripRepository;
this.tripService = tripService;
this.observedLocationService = observedLocationService;
this.operationGroupRepository = operationGroupRepository;
this.measurementDao = measurementDao;
......@@ -222,7 +219,7 @@ public class AggregatedLandingServiceImpl implements AggregatedLandingService {
});
// Check all activity have date without time
aggregatedLandings.forEach(aggregatedLanding -> aggregatedLanding.getVesselActivities()
.forEach(activity -> Preconditions.checkArgument(activity.getDate().equals(Dates.resetTime(activity.getDate())))));
.forEach(activity -> Preconditions.checkArgument(activity.getDate().equals(Dates.resetTime(activity.getDate())), "Must have a date without time")));
// Load VesselSnapshot Entity
aggregatedLandings.parallelStream()
......@@ -291,14 +288,7 @@ public class AggregatedLandingServiceImpl implements AggregatedLandingService {
}
if (!landingIdsToRemove.isEmpty()) {
// Delete remaining landings
// Delete linked trips
tripRepository.deleteByLandingIds(landingIdsToRemove);
// Delete landing
landingRepository.deleteByIds(landingIdsToRemove);
// TODO LP: or use this
//landingIdsToRemove.forEach(landingService::delete);
landingService.delete(landingIdsToRemove);
// Add the observed location to check list
observationIdsToCheck.add(observedLocation.getId());
......@@ -396,8 +386,8 @@ public class AggregatedLandingServiceImpl implements AggregatedLandingService {
DataBeans.setDefaultRecorderPerson(landing, parent.getRecorderPerson());
LandingVO savedLanding = landingService.save(landing);
if (landing.getMeasurementValues() != null)
measurementDao.saveLandingMeasurementsMap(savedLanding.getId(), landing.getMeasurementValues());
// if (landing.getMeasurementValues() != null)
// measurementDao.saveLandingMeasurementsMap(savedLanding.getId(), landing.getMeasurementValues());
}
......@@ -438,10 +428,7 @@ public class AggregatedLandingServiceImpl implements AggregatedLandingService {
landings = getLandings(observedLocationId);
}
// Delete landings
landingRepository.deleteByIds(Beans.collectIds(landings));
// TODO LP or use this:
Beans.collectIds(landings).forEach(landingService::delete);
landingService.delete(Beans.collectIds(landings));
// Delete observed location
observedLocationService.delete(observedLocationId);
......@@ -642,6 +629,7 @@ public class AggregatedLandingServiceImpl implements AggregatedLandingService {
if (trip.getLanding() == null) {
trip.setLanding(landing);
}
trip.setLandingId(landing.getId());
TripVO savedTrip = saveTrip(trip);
if (activity.getTripId() == null)
activity.setTripId(savedTrip.getId());
......@@ -654,7 +642,7 @@ public class AggregatedLandingServiceImpl implements AggregatedLandingService {
} else if (tripId != null) {
// Delete the whole trip
tripRepository.deleteById(tripId);
tripService.delete(tripId);
if (log.isDebugEnabled()) {
log.debug(String.format("Trip (id=%s) successfully deleted", tripId));
}
......@@ -670,8 +658,11 @@ public class AggregatedLandingServiceImpl implements AggregatedLandingService {
Preconditions.checkNotNull(trip);
Preconditions.checkNotNull(trip.getLanding());
// Trip itself (landing updated by tripRepository)
TripVO savedTrip = tripRepository.save(trip);
// Trip itself (landing updated by tripService)
TripVO savedTrip = tripService.save(
trip,
TripSaveOptions.builder().withLanding(true).build() // TODO check if needed, landing should be already created
);
// Save metiers
operationGroupRepository.saveMetiersByTripId(savedTrip.getId(), trip.getMetiers());
......@@ -693,7 +684,7 @@ public class AggregatedLandingServiceImpl implements AggregatedLandingService {
private TripVO loadTrip(int tripId) {
//noinspection UnnecessaryBoxing
TripVO trip = tripRepository.get(Integer.valueOf(tripId));
TripVO trip = tripService.get(Integer.valueOf(tripId));
// Load metiers and operation groups
if (CollectionUtils.isEmpty(trip.getMetiers())) {
trip.setMetiers(operationGroupRepository.getMetiersByTripId(tripId));
......@@ -747,8 +738,12 @@ public class AggregatedLandingServiceImpl implements AggregatedLandingService {
}
private void loadLandingMeasurements(LandingVO landing) {
if (landing.getMeasurementValues() == null)
landing.setMeasurementValues(measurementDao.getLandingMeasurementsMap(landing.getId()));
if (landing.getMeasurementValues() == null) {
Map<Integer, String> result = new HashMap<>();
Optional.ofNullable(measurementDao.getLandingMeasurementsMap(landing.getId())).ifPresent(result::putAll);
Optional.ofNullable(measurementDao.getSurveyMeasurementsMap(landing.getId())).ifPresent(result::putAll);
landing.setMeasurementValues(result);
}
}
private LandingVO createLanding(ObservedLocationVO parent, Integer vesselId, VesselActivityVO activity) {
......
......@@ -57,8 +57,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
......@@ -213,6 +211,9 @@ public class LandingServiceImpl implements LandingService {
// Delete linked trips
tripRepository.deleteByLandingId(id);
measurementDao.deleteMeasurements(LandingMeasurement.class, Landing.class, ImmutableList.of(id));
measurementDao.deleteMeasurements(SurveyMeasurement.class, Landing.class, ImmutableList.of(id));
// Delete landing
landingRepository.deleteByIds(ImmutableList.of(id));
......
......@@ -525,7 +525,8 @@ public class TripServiceImpl implements TripService {
landing.setDateTime(trip.getReturnDateTime());
landing.setObservers(Beans.getSet(trip.getObservers()));
landingRepository.save(landing);
// Don't check update date and don't lock
landingRepository.save(landing, false, false);
trip.setLandingId(landing.getId());
}
......
......@@ -190,7 +190,7 @@ public class VesselServiceImpl implements VesselService {
Preconditions.checkNotNull(source.getVesselRegistrationPeriod().getRegistrationLocation().getId(), "Missing registration location");
}
VesselVO savedVessel = vesselRepository.save(source, checkUpdateDate);
VesselVO savedVessel = vesselRepository.save(source, checkUpdateDate, true);
if (savedVessel.getVesselFeatures() != null) {
VesselFeaturesVO savedVesselFeatures = vesselFeaturesRepository.save(savedVessel.getVesselFeatures());
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment