Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APT does not accept @Embeddable Java Records #3695

Open
KlausUnger opened this issue Mar 2, 2024 · 3 comments
Open

APT does not accept @Embeddable Java Records #3695

KlausUnger opened this issue Mar 2, 2024 · 3 comments
Labels

Comments

@KlausUnger
Copy link

Problemdescription

Compile-Error when a Java-Record is marked as @embeddable!

Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project jpa-embeddable-records-demo: Fatal error compiling: java.lang.IllegalArgumentException: Illegal type: at.spengergasse.jpaembeddablerecordsdemo.EmailAddress

Steps to reproduce

Generate a Spring Boot 3.2.3 Starter project including:

  • Lombok
  • Data-JPA
  • H2
  • Validation

Use config-link to reproduce setup

Add Entity

package at.spengergasse.jpaembeddablerecordsdemo;


import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.jpa.domain.AbstractPersistable;

import java.util.ArrayList;
import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder

@Entity
@Table(name = "persons")
public class Person extends AbstractPersistable<Long> {
    private @Version Integer version;
    private @Embedded EmailAddress username;
    private String password;

    @ElementCollection
    @CollectionTable(name = "person_email_addresses",
            joinColumns = @JoinColumn(foreignKey = @ForeignKey(name = "FK_person_email_addresses_2_person")))
    @Builder.Default
    private List<EmailAddress> emailAddresses = new ArrayList<>(3);

    public Person addEmailAddress(String eMail) {
        return addEmailAddress(new EmailAddress(eMail));
    }

    public Person addEmailAddress(EmailAddress emailAddress) {
        this.emailAddresses.add(emailAddress);
        return this;
    }
}

Add Embeddable Record

package at.spengergasse.jpaembeddablerecordsdemo;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

@Embeddable
public record EmailAddress(
        @Column(name="email_address")
        @Email @NotBlank String value) {
}

Add Repository

package at.spengergasse.jpaembeddablerecordsdemo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PersonRepository extends JpaRepository<Person, Long> {
}

Add Repository Test

package at.spengergasse.jpaembeddablerecordsdemo;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

@DataJpaTest
class PersonRepositoryTest {

    private @Autowired PersonRepository personRepository;

    @Test
    void ensureSave() {
        Person p = Person.builder()
                         .username(new EmailAddress("u@spg.at"))
                         .password("geheim")
                         .build()
                         .addEmailAddress("unger@spg.at");

        personRepository.save(p);

        assertThat(p.getId()).isNotNull();
        assertThat(p.getVersion()).isNotNull();
    }
}

compiles green, tests green

Add QueryDSL dependencies

compile fails

Environment

  • current Mac OSX
  • current Intellij Ultimate

Querydsl version:

5.0.0 and 5.1.0

Querydsl module:

querydsl-apt

Database:

  • H2
  • Postgresql

JDK:

  • 21

Additional details

Embeddable Java Record works fine with default Spring-Data-JPA/Hibernate/H2-or-Postgresql setup!
Compile-Error occurs after adding querydsl support and using apt to generate QClasses!

@KlausUnger KlausUnger added the bug label Mar 2, 2024
@KUnger-Spg
Copy link

Possible Workaround

Provide an AttributeConverter<EmailAddress, String> and get rid of @Embeddable/@Embedded annotations on the type and in the entity!

Updated Java-Record based rich type

package at.spengergasse.jpaembeddablerecordsdemo;

import jakarta.persistence.Column;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

public record EmailAddress(
        @Column(name="email_address")
        @Email @NotBlank String value) {
}

Updated Entity

package at.spengergasse.jpaembeddablerecordsdemo;


import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.jpa.domain.AbstractPersistable;

import java.util.ArrayList;
import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder

@Entity
@Table(name = "persons")
public class Person extends AbstractPersistable<Long> {
    private @Version Integer version;
    private /* @Embedded deleted */ EmailAddress username;
    private String password;

    @ElementCollection
    @CollectionTable(name = "person_email_addresses",
            joinColumns = @JoinColumn(foreignKey = @ForeignKey(name = "FK_person_email_addresses_2_person")))
    @Builder.Default
    private List<EmailAddress> emailAddresses = new ArrayList<>(3);

    public Person addEmailAddress(String eMail) {
        return addEmailAddress(new EmailAddress(eMail));
    }

    public Person addEmailAddress(EmailAddress emailAddress) {
        this.emailAddresses.add(emailAddress);
        return this;
    }
}

Converter

package at.spengergasse.jpaembeddablerecordsdemo;

import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

@Converter(autoApply = true)
public class EmailAddressConverter implements AttributeConverter<EmailAddress, String> {
    @Override
    public String convertToDatabaseColumn(EmailAddress attribute) {
        if (attribute == null) return null;
        return attribute.value();
    }

    @Override
    public EmailAddress convertToEntityAttribute(String dbData) {
        if (dbData == null) return null;
        return new EmailAddress(dbData);
    }
}

Reasonably generated QPerson class

package at.spengergasse.jpaembeddablerecordsdemo;

import static com.querydsl.core.types.PathMetadataFactory.*;

import com.querydsl.core.types.dsl.*;

import com.querydsl.core.types.PathMetadata;
import javax.annotation.processing.Generated;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.dsl.PathInits;


/**
 * QPerson is a Querydsl query type for Person
 */
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QPerson extends EntityPathBase<Person> {

    private static final long serialVersionUID = 1966126792L;

    public static final QPerson person = new QPerson("person");

    public final org.springframework.data.jpa.domain.QAbstractPersistable _super = new org.springframework.data.jpa.domain.QAbstractPersistable(this);

    public final ListPath<EmailAddress, SimplePath<EmailAddress>> emailAddresses = this.<EmailAddress, SimplePath<EmailAddress>>createList("emailAddresses", EmailAddress.class, SimplePath.class, PathInits.DIRECT2);

    public final NumberPath<Long> id = createNumber("id", Long.class);

    public final StringPath password = createString("password");

    public final SimplePath<EmailAddress> username = createSimple("username", EmailAddress.class);

    public final NumberPath<Integer> version = createNumber("version", Integer.class);

    public QPerson(String variable) {
        super(Person.class, forVariable(variable));
    }

    public QPerson(Path<? extends Person> path) {
        super(path.getType(), path.getMetadata());
    }

    public QPerson(PathMetadata metadata) {
        super(Person.class, metadata);
    }
}

Compiles without errors and all tests are green!

@KlausUnger
Copy link
Author

Link to DemoRepo

Demo Repo

@velo
Copy link
Contributor

velo commented Apr 13, 2024

FWIW, my fork should be more friendly to java records
https://github.com/OpenFeign/querydsl

If that still falls short, I'm happy to take contributions and get them released.

Good luck

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants