I have a problem with neo4j database. When I try to init data, it should be just create one sample data but sometimes when I try to init data, it create double sample data. There is no trace about calling second times. This is the Config of my Neo4j
@Configuration
@EnableNeo4jRepositories(basePackages = "com.example.neo.repository")
@EnableTransactionManagement
public class Neo4jConfig extends Neo4jConfiguration {
@Override
@Bean
public SessionFactory getSessionFactory() {
// with domain entity base package(s)
return new SessionFactory("com.example.neo.model", "BOOT-INF.classes.com.example.neo.model");
}
// needed for session in view in web-applications
@Override
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
}
This is how I called my function
@RequestMapping(value = "/initCurrency")
public ModelAndView initCurrency() {
initializationService.initCurrency();
ModelAndView model = new ModelAndView("redirect:/");
return model;
}
This is the initializationService function
private String[][] currencyList = {
{ "USD", "7.5" },
{ "DKK", "1" },
{ "AFN", "1"},{ "EUR", "1"},{ "ALL", "1"},{ "DZD", "1"},{ "USD", "1"},{ "AOA", "1"},{ "XCD", "1"},
{ "ARS", "1"},{ "AMD", "1"},{ "AWG", "1"},{ "SHP", "1"},{ "AUD", "1"},{ "AZN", "1"},{ "BSD", "1"},
{ "BHD", "1"},{ "BDT", "1"},{ "BBD", "1"}
}
@Override
public void initCurrency() {
for (String[] currency : currencyList) {
Currency existCurrency = currencyService.findByName(currency[0]);
if (existCurrency == null) {
existCurrency = new Currency(currency[0], Double.valueOf(currency[1]));
currencyService.save(existCurrency);
}
}
}
The only reliable way to avoid duplicates is to have an actual unicity constraint on the property:
CREATE CONSTRAINT ON (n:Currency) ASSERT n.name IS UNIQUE;
There's no way in SDN 4.[0-2] (yet) to create such a constraint from the model (there was in SDN 3.x with the @Indexed
or @Indexed(unique = true)
annotation), so you'll have to run the query independently (for example using Liquigraph!).
Doing a lookup to guard the creation is not enough in a concurrent environment (sequential calls are OK), because there's no lock between the read and the write, which can result in the following scenario with an interleaved execution:
Since you end up with duplicates, 2 concurrent calls are happening. A load balancer with a really short timeout and configured to retry? Activate HTTP logs, add some logging in the Spring controller or service, capture the traffic with tcpdump
, etc. It will be easier to isolate the second call once the unicity constraint is active, since you'll get an exception.