4

I have a JPA web application with some integration tests against the JPA repositories. There are no integration tests against the Neo4J repositories yet.

Now, I have added some Neo4J functionality to this existing JPA web application.

I'm this now using Neo4J repositories, alongside JPA repositories. My entities and repositories are named differently and are sitting in different packages.

My tests all extend the following class:

@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ApplicationConfiguration.class, WebSecurityTestConfiguration.class, WebConfiguration.class })
@Transactional
public abstract class AbstractControllerTest {
...
}

The tests run fine when the application configuration does not have any

Neo4J configuration:
@Configuration
@ComponentScan(basePackages = { "it.robot.rest.config" })
@Import({ DatabaseConfiguration.class, Log4jWeb.class })
public class ApplicationConfiguration {
}

But they error on an exception when adding the Neo4J configuration:

@Configuration
@ComponentScan(basePackages = { "it.robot.rest.config" })
@Import({ DatabaseConfiguration.class, Neo4JRepositoryConfiguration.class, Log4jWeb.class })
public class ApplicationConfiguration {
}

The exception is:

javax.persistence.TransactionRequiredException: no transaction is in progress

Here is the Neo4J configuration (I tried both Neo4jConfiguration and CrossStoreNeo4jConfiguration classes and I get the same exception):

@Configuration
@EnableNeo4jRepositories(basePackages = { "it.robot.data.neo4j.repository" } )
@EnableTransactionManagement
@ComponentScan(basePackages = { "it.robot.data.neo4j.service" })
public class Neo4JRepositoryConfiguration extends Neo4jConfiguration {

  public static final String URL = "http://localhost:7474/db/data/";
  public static final String LOGIN = "neo4j";
  public static final String PASSWORD = "mypassword";

  Neo4JRepositoryConfiguration() {
    setBasePackage("it.robot.data.neo4j.domain"); 
  }

  @Bean
  GraphDatabaseService graphDatabaseService() { 
    return new SpringCypherRestGraphDatabase(URL, LOGIN, PASSWORD);
  }

}

Here is the JPA configuration is:

@Configuration
@Import({ JpaService.class, Log4j.class })
@EnableTransactionManagement
@ComponentScan(basePackages = { "it.robot.data.config" })
@EnableJpaRepositories(basePackages = { "it.robot.data.jpa" }, repositoryFactoryBeanClass  = it.robot.data.jpa.repository.GenericRepositoryFactoryBean.class)
public class DatabaseConfiguration {
...
}

It looks like the Neo4jConfiguration class transaction manager has the same name ("transactionManager") as the JPA transaction manager, and overrides it.

I would content myself with Neo4J using the JPA transaction manager provided by Spring but I wonder if that is possible.

Some additional information...

I'm using spring-data-neo4j and spring-data-neo4j-rest version 3.3.2.RELEASE

I'm using a server Neo4J database and not an embedded one and of course the Neo4J server is started.

I disabled the authentication on the database since it was standing in my way and my curl request didn't seem to update the password:

curl -H "Accept:application/json" 
 -H "Content-Type: application/json" 
 "http://localhost:7474/user/neo4j/password" 
 -X POST -d "{ \"password\" : \"myownpassword\" }"

The only user I know of doesn't seem to be too vocal:

stephane@stephane-ThinkPad-X301:~> curl -H "Accept:application/json" -H "Content-Type: application/json" "http://localhost:7474/user/neo4j"
stephane@stephane-ThinkPad-X301:~> 
stephane@stephane-ThinkPad-X301:~>

I have not created any "schema/structure" in the graph and am not sure if I should do.

The Neo4J entities:

@NodeEntity
@SequenceGenerator(name = "id_generator", sequenceName = "sq_id_part")
public class Neo4JPart extends BaseEntity {

  @Column(nullable = false)
  private String name;
  @Column(nullable = false, unique = true)
  private String serialNumber;
  private Integer weight;
  @ManyToOne
  @JoinColumn(name = "manufacturer_id", nullable = false)
  private Neo4JManufacturer manufacturer;
  @Fetch
  @RelatedTo(type = "part", direction = Direction.BOTH)
  public Set<Neo4JPart> parts;

  public Neo4JPart() {
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getSerialNumber() {
    return serialNumber;
  }

  public void setSerialNumber(String serialNumber) {
    this.serialNumber = serialNumber;
  }

  public Integer getWeight() {
    return weight;
  }

  public void setWeight(Integer weight) {
    this.weight = weight;
  }

  public Neo4JManufacturer getManufacturer() {
    return manufacturer;
  }

  public void setManufacturer(Neo4JManufacturer manufacturer) {
    this.manufacturer = manufacturer;
  }

  public Set<Neo4JPart> getParts() {
    return parts;
  }

  public void setParts(Set<Neo4JPart> parts) {
    this.parts = parts;
  }

  public String toString() {
    String results = name + "'s compatible parts include\n";
    if (parts != null) {
      for (Neo4JPart part : parts) {
        results += "\t- " + part.name + "\n";
      }
    }
    return results;
  }

}

@MappedSuperclass
public class BaseEntity {

  @GraphId
  @GeneratedValue(strategy = GenerationType.AUTO, generator = "id_generator")
  @Column(name = "id")
  private Long id;

  @Version
  @Column(nullable = false)
  private int version;

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public int getVersion() {
    return this.version;
  }

  public void setVersion(int version) {
    this.version = version;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }

    if (this.id == null || obj == null || !(this.getClass().equals(obj.getClass()))) {
      return false;
    }

    BaseEntity that = (BaseEntity) obj;

    return this.id.equals(that.getId());
  }

  @Override
  public int hashCode() {
    return id == null ? 0 : id.hashCode();
  }

}

And the Neo4J repositories:

public interface Neo4JPartRepository extends GraphRepository<Neo4JPart> {

  public Neo4JPart findByName(String name);

  public Neo4JPart findBySerialNumber(String serialNumber);

  public Page<Neo4JPart> findByManufacturer(@Param("manufacturer") Neo4JManufacturer manufacturer, Pageable page);

  public List<Neo4JPart> findByManufacturer(@Param("manufacturer") Neo4JManufacturer manufacturer);

  public Page<Neo4JPart> findByPartsName(String name, Pageable page);

}

public interface Neo4JManufacturerRepository extends GraphRepository<Neo4JManufacturer> {

  Neo4JManufacturer findByName(String name);

}

The Maven dependencies are:

  <org.springframework.version>4.1.2.RELEASE</org.springframework.version>
  <hibernate.version>4.3.6.Final</hibernate.version>

  <dependencies>
    <dependency>
      <groupId>com.thalasoft</groupId>
      <artifactId>toolbox</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.2.1</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.34</version>
    </dependency>
    <dependency> 
      <groupId>com.oracle</groupId> 
      <artifactId>ojdbc6</artifactId> 
      <version>11.2.0.3</version> 
    </dependency>
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.3.172</version>
    </dependency>
    <dependency>
      <groupId>org.hsqldb</groupId>
      <artifactId>hsqldb</artifactId>
      <version>2.3.2</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate.javax.persistence</groupId>
      <artifactId>hibernate-jpa-2.1-api</artifactId>
      <version>1.0.0.Final</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-jpa</artifactId>
      <version>1.6.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>com.mysema.querydsl</groupId>
      <artifactId>querydsl-core</artifactId>
      <version>3.5.0</version>
    </dependency>
    <dependency>
      <groupId>com.mysema.querydsl</groupId>
      <artifactId>querydsl-apt</artifactId>
      <version>3.5.0</version>
    </dependency>
    <dependency>
      <groupId>com.mysema.querydsl</groupId>
      <artifactId>querydsl-jpa</artifactId>
      <version>3.5.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-commons</artifactId>
      <version>1.10.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>1.1.0.Final</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.6.4</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.6.4</version>
    </dependency>
    <dependency>
      <groupId>org.lazyluke</groupId>
      <artifactId>log4jdbc-remix</artifactId>
      <version>0.2.7</version>
    </dependency>
    <dependency>
      <groupId>javax.mail</groupId>
      <artifactId>mail</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>joda-time</groupId>
      <artifactId>joda-time</artifactId>
      <version>2.5</version>
    </dependency>
    <dependency>
      <groupId>org.jadira.usertype</groupId>
      <artifactId>usertype.jodatime</artifactId>
      <version>2.0.1</version>
    </dependency>
    <dependency>
      <groupId>javax.transaction</groupId>
      <artifactId>jta</artifactId>
      <version>1.1</version>
    </dependency>
    <dependency>
      <groupId>org.jasypt</groupId>
      <artifactId>jasypt</artifactId>
      <version>1.7</version>
    </dependency>
    <dependency>
      <groupId>org.assertj</groupId>
      <artifactId>assertj-core</artifactId>
      <version>1.6.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${org.springframework.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-oxm</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-neo4j</artifactId>
      <version>3.3.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-neo4j-rest</artifactId>
      <version>3.3.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-neo4j-cross-store</artifactId>
      <version>3.3.2.RELEASE</version>
    </dependency>
  </dependencies>

I tried to upgrade to the 3.4.0 version of the newly available release on search.maven.org but the build now gives the following exception:

AnnotationFormatError: Invalid default: public abstract java.lang.Class org.springframework.data.neo4j.config.EnableNeo4jRepositories.repositoryBaseClass()

I could see nothing about that repositoryBaseClass in the reference documentation http://docs.spring.io/spring-data/neo4j/docs/3.4.0.RELEASE/reference/pdf/spring-data-neo4j-reference.pdf

The source code Javadoc only says:

Configure the repository base class to be used to create repository proxies for this particular configuration.

And that left me scratching my head wondering what is a repository proxy and if one is required in my case.

Stephane
  • 11,836
  • 25
  • 112
  • 175
  • Can you share your dependency config? I.e. pom.xml or build.gradle ? – Michael Hunger Aug 30 '15 at 15:05
  • have you looked at http://stackoverflow.com/questions/28846157/spring-data-jpa-transaction-no-transaction-in-progress-spring-data-neo4j ? it may help you – Supamiu Sep 03 '15 at 08:17
  • @Supamiu Yes I had done that, and even had left a comment in there. But I'm still stuck. It's a show stopper for me. Strange that I cannot seem to find any example on how to do such a basic thing on the net. I've been searching for days now. – Stephane Sep 03 '15 at 19:12
  • @MichaelHunger Did the pom help any ? – Stephane Sep 03 '15 at 19:13

1 Answers1

0

I could solve the issue with the solution provided at Implementing Spring ChainedTransactionManager according to the "best efforts 1PC" pattern with a chained transaction manager, following a tip by Simon at How do I properly set up cross-store persistence using Spring Data JPA + Neo4j?

I just had to change my Neo4j configuration. I didn't even have to touch anything in the other JPA transaction manager.

Here is my Neo4j configuration:

@EnableNeo4jRepositories(basePackages = { "it.robot.data.neo4j.repository" })
@EnableTransactionManagement
@ComponentScan(basePackages = { "it.robot.data.neo4j.service" })
public class Neo4JRepositoryConfiguration extends Neo4jConfiguration {

  private static Logger logger = LoggerFactory.getLogger(Neo4JRepositoryConfiguration.class);

  public static final String URL = "http://localhost:7474/db/data/";
  public static final String LOGIN = "neo4j";
  public static final String PASSWORD = "xxxxx";

  Neo4JRepositoryConfiguration() {
    setBasePackage("it.robot.data.neo4j.domain");
  }

  @Bean
  GraphDatabaseService graphDatabaseService() {
    return new SpringCypherRestGraphDatabase(URL, LOGIN, PASSWORD);
  }

  @Autowired
  LocalContainerEntityManagerFactoryBean entityManagerFactory;

  @Override
  public PlatformTransactionManager neo4jTransactionManager(
      GraphDatabaseService graphDatabaseService) {
    return new ChainedTransactionManager(
        new JpaTransactionManager(entityManagerFactory.getObject()),
        new JtaTransactionManagerFactoryBean(graphDatabaseService).getObject());
  }

}
Community
  • 1
  • 1
Stephane
  • 11,836
  • 25
  • 112
  • 175