Hot questions for Using AspectJ in hibernate

Question:

How can I use Hibernate validator to validate the arguments inside the constructor or method? I want the validation to occur before the ValueObject creation so I can throw an exception and not create the object unless all parameters are valid.

Basically I'm trying to use annotations instead of doing something like this if possible:

public class ConditionalPerson {
    private String name;
    private String surname;
    private int age;

    public ConditionalPerson(String name, String surname, int age){
        if (name == null || surname == null || age < 1) {
            throw new IllegalArgumentException();
        }
        this.name = name;
        this.surname = surname;
        this.age = age;
    }
}

I've tried following the docs like this which seems to work but still results in the object being created.

public class Person {
    @NotNull(message = "Name can't be null")
    @NotEmpty(message = "Name can't be empty")
    @Length(min=1)
    private String name;

    @NotNull(message = "Surname can't be null")
    @NotEmpty(message = "Surname can't be empty")
    @Length(min=1)
    private String surname;

    @Range(min=100, max=200)
    private int age;

    public Person(String name, String surname, int age){
        this.name = name;
        this.surname = surname;
        this.age = age;
    }
}

Adding the annotations to the constructor arguments seems to have no effect

public Person(@NotNull String name, 
              @NotNull String surname, 
              @Range(min=100, max=200) int age) { 
    ...
}

How I'm creating the Objects:

public class Example {
    Person person;
    ConditionalPerson person2;

    public static void main(String[] args) {
        Example example = new Example();
        example.makePerson();
        example.makeConditionalPerson();
    }

    public void makePerson() {
        person = new Person(null, "", 12);
        Validator validator = ValidatorSingleton.getValidator();

        Set<ConstraintViolation<Person>> violations = validator.validate(person);

        if (violations.size() > 0) {
            throw new IllegalArgumentException();
        }
    }

    public void makeConditionalPerson() {
        person2 = new ConditionalPerson(null, "", 123);
    }
}

Validator:

public class ValidatorSingleton {
    private static final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    private static final javax.validation.Validator validator = factory.getValidator();

    private ValidatorSingleton(){}

    public static Validator getValidator() {
        return validator;
    }
}

Answer:

For anyone else that finds this post. I changed my approach slightly and got this working using OVal Validation & AspectJ instead of Hibernate.

Basically the same example as above except I needed to add @Guarded above the class:

@Guarded
public class Person {
    private String name;
    private String surname;
    private int age;

    public Person(@NotNull String name, @NotNull String surname, @Range(min=100, max=200) int age){
        this.name = name;
        this.surname = surname;
        this.age = age;
    }
}

Then in your build.gradle add:

buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.10'
    }
}
dependencies {
    compile 'org.aspectj:aspectjrt:1.8.1'
    compile 'net.sf.oval:oval:1.86'
}

tasks.withType(JavaCompile) {
    doLast {
        String[] args = ["-showWeaveInfo",
                         "-1.8",
                         "-inpath", destinationDir.toString(),
                         "-aspectpath", classpath.asPath,
                         "-d", destinationDir.toString(),
                         "-classpath", classpath.asPath]

        MessageHandler handler = new MessageHandler(true);
        new Main().run(args, handler)
    }

Question:

I would like to record (almost) all database activity for HTTP requests in a Spring/Hibernate web application using an @Aspect and HandlerInterceptor.

Ideally, I could wrap the @Aspect @Around calls to java.sql.Statement.execute, but since that class is Java and may not use the same class loader, I have to settle for second best.

I have an @Aspect working for calls to JdbcTemplate using the execution(* org.springframework.jdbc.core.JdbcOperations.*(String, ..)) pointcut as explained here, but I also need any database activity invoked by Hibernate.

Does anybody have advice on this? I'm up for any suggestions.


Answer:

My suggestion is to use AspectJ instead of Spring AOP. Its usage from within Spring applications is well documented. You would have then full AOP power, e.g. you can use call() pointcuts and many more instead of only execution(), apply your aspects to classes which are not Spring Beans and so forth.

Question:

I have some classes marked with @Configurable annotation with @Autowired fields and some of them works fine but other throw NullPointerException due to fields not getting @Autowired correctly.

My configuration is like this

@Configuration
@EnableSpringConfigured
@EnableLoadTimeWeaving(aspectjWeaving = AspectJWeaving.ENABLED)
@EnableAsync(mode = AdviceMode.PROXY, proxyTargetClass = true)
@Import({
    ...
})
@ComponentScan(
    basePackageClasses = {
        ...
    }
)
public class RootConfig implements AsyncConfigurer {

aop.xml:

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <weaver>
        <!-- only weave classes in our application-specific packages -->
        <include within="aero.geoscan..*"/>
    </weaver>
</aspectj>

Answer:

Spring may load weaver bean lately and thus not process all neccessary classes. Try to add @DependsOn("loadTimeWeaver") to your @Configuration.

And if it doesn't help add <weaver options="-verbose -debug -showWeaveInfo"> to your aop.xml. It will help to resolve problem by providing additional logs

Question:

I'm working with Hibernate in a Spring Boot project.

I have the following piece of code:

public class SomeService {

    private Dependency dependency;

    @Transactional(readOnly=false)
    public void doSomething() {
        //some BL code...

        dependency.doSomeDbManipualation();

        someOperation();
    }

    public void someOperation() {
        //some code that eventually fails
    }
}

public class Dependency {

    public void doSomeDbManipulation() {
        Entity entity = ...; //get the entity from current session by its key
        if (entity != null) {
            session.delete(entity);
        }

        OtherEntity oEntity = new OtherEntity();
        //set its fields
        Long oEntityId = session.save(oEntity);

        entity = new Entity();
        entity.setForeignKey(oEntityId);
        //set other fields
        session.persist(entity);
    }
}

Now, I have in the database an entity with the relevant key. So I expect that when calling the service, the code that looks for the entity will indeed find it. But since someOperation() fails, I expect to see no change in the DB.

In fact, after calling someService.doSomething() (and failing), I look in the DB and I see that the existing entity was deleted! But a new entity was not created (which is ok).

Why is this transaction "half committed"?

EDIT: Apparently delete() and save() are committed immediately. When I debug, I see that entity is immediately deleted after this line in code is done. Also OtherEntity is added immediately to the DB. persist() is not committed immediately.

I use AspectJ for transaction management. Here is the relevant part from my pom.xml:

<project>
    ...
    <dependencies>
        ...
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
        ...
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.7</version>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>1.8.3</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <showWeaveInfo>true</showWeaveInfo>
                    <outxml>true</outxml>
                    <forceAjcCompile>true</forceAjcCompile>
                    <source>1.8</source>
                    <target>1.8</target>
                    <Xlint>ignore</Xlint>
                    <complianceLevel>1.8</complianceLevel>
                    <encoding>UTF-8</encoding>
                    <verbose>true</verbose>
                    <preserveAllLocals>true</preserveAllLocals>
                    <aspectLibraries>
                        <aspectLibrary>
                            <groupId>org.springframework</groupId>
                            <artifactId>spring-aspects</artifactId>
                        </aspectLibrary>
                    </aspectLibraries>
                </configuration>
                <executions>
                    <execution>
                        <id>AspectJ-Compile</id>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
        ...
    </build>
</project>

Answer:

I don't know why aspect j is explicitly added as dependency in pom file.if u r using spring boot u will get aspects out of the box.I have seen unpredictable behavior when duplicate jars are present in the classpath.

Also I failed to understand why the DB manipulation method is called from a method which itself is marked for read-only transaction.

I feel design is not correct here.