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

Jakarta Data does not work #2209

Open
hantsy opened this issue Jul 12, 2024 · 5 comments
Open

Jakarta Data does not work #2209

hantsy opened this issue Jul 12, 2024 · 5 comments

Comments

@hantsy
Copy link

hantsy commented Jul 12, 2024

Describe the bug
I tried to port my Jakarta Data example project from Hibernate to Jakarta EE/EclipseLink. But it failed when testing with Glassfish v8 and EclipseLink.

To Reproduce
Steps/resources to reproduce the behavior:

  • EclipseLink version 5.0.0 B02
  • Java/JDK version 21
  • Entity source (mainly applied annotations)
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "posts")
public class Post implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    UUID id;

    @Basic(optional = false)
    String title;

    @Basic(optional = false)
    String content;

    @Enumerated
    @Builder.Default
    Status status = Status.DRAFT;

    @OneToMany(mappedBy = "post", targetEntity = Comment.class, cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    List<Comment> comments;

    LocalDateTime createdAt;
    LocalDateTime lastModifiedAt;

    @PrePersist
    public void prePersist() {
        createdAt = LocalDateTime.now();
        lastModifiedAt = createdAt;
    }

    @PreUpdate
    public void preUpdate() {
        lastModifiedAt = LocalDateTime.now();
    }

    @Override
    public String toString() {
        return "Post{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", createdAt=" + createdAt +
                ", lastModifiedAt=" + lastModifiedAt +
                '}';
    }
}


@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "comments")
public class Comment implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    UUID id;

    @Basic(optional = false)
    private String content;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "post_id")
    private Post post;

    private LocalDateTime createdAt;

    @PrePersist
    public void prePersist() {
        this.createdAt = LocalDateTime.now();
    }
}

And I created a free style @Repository interface like this.

@Repository
public interface Blogger {
//
//    StatelessSession session();

    @Query("""
            SELECT p.id, p.title, size(c) AS summary FROM Post p LEFT JOIN p.comments c
            WHERE p.title LIKE :title
                OR p.content LIKE :title
                OR c.content LIKE :title
            GROUP BY p
            ORDER BY p.createdAt DESC
            """)
    Page<PostSummary> allPosts(@Param("title") String title, PageRequest page);

    @Find
    @OrderBy("createdAt")
    List<Post> byStatus(Status status, Order<Post> order, Limit limit);

    @Find
    Optional<Post> byId(UUID id);

    @Insert
    Post insert(Post post);

    @Insert
    Comment insert(Comment comment);

    @Update
    Post update(Post post);

    @Delete
    void delete(Post post);

    // see: https://hibernate.zulipchat.com/#narrow/stream/132096-hibernate-user/topic/Jakarta.20Data.20cascade.20does.20not.20work.20in.20custom.20deletion.20Query/near/441874793
    @Query("delete from Post")
    @Transactional
    long deleteAllPosts();

//    default List<Comment> getCommentsOfPost(UUID postId) {
//        var post = this.byId(postId).orElseThrow(() -> new PostNotFoundException(postId));
//        session().fetch(post.getComments());
//        return post.getComments();
//    }
}

I have written a basic test to verify it, failed.

@ExtendWith(ArquillianExtension.class)
public class BloggerTest {

    private final static Logger LOGGER = Logger.getLogger(BloggerTest.class.getName());

    @Deployment
    public static WebArchive createDeployment() {
        WebArchive war = ShrinkWrap.create(WebArchive.class)
                .addPackage(Post.class.getPackage())
                .addAsResource("test-persistence.xml", "META-INF/persistence.xml")
                .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
        LOGGER.log(Level.INFO, war.toString(true));
        return war;
    }

    @PersistenceContext
    private EntityManager em;

    @Inject
    private Blogger blogger;

    @Inject
    UserTransaction ux;

    private void startTx() throws Exception {
        ux.begin();
        em.joinTransaction();
    }

    @AfterEach
    public void after() throws Exception {
        endTx();
    }

    private void endTx() throws Exception {
        LOGGER.log(Level.INFO, "Transaction status: {0}", ux.getStatus());
        try {
            if (ux.getStatus() == Status.STATUS_ACTIVE) {
                ux.commit();
            }
        } catch (Exception e) {
            ux.rollback();
        }
    }

    @Test
    public void testBlogger() throws Exception {
        var post = new Post();
        post.setTitle("My Post");
        post.setContent("My Post Content");
        startTx();
        blogger.insert(post);
        endTx();
        assertNotNull(post.getId());

        var foundPost = blogger.byId(post.getId());
        assertTrue(foundPost.isPresent());
        Post savedPost = foundPost.get();
        assertEquals(post.getTitle(), savedPost.getTitle());
        assertEquals(post.getContent(), savedPost.getContent());

        var foundByStatus = blogger.byStatus(
                com.example.Status.PUBLISHED,
                Order.by(Sort.desc("createdAt")),
                Limit.of(10)
        );
        assertEquals(1, foundByStatus.size());
        assertEquals(post.getId(), foundByStatus.getFirst().getId());

        var allPosts = blogger.allPosts("%My%", PageRequest.ofPage(1, 10, true));
        assertEquals(1, allPosts.totalElements());
        assertEquals(post.getId(), allPosts.content().getFirst().id());

        savedPost.setTitle("New Title");
        startTx();
        blogger.update(savedPost);
        endTx();

        var updatedPost = blogger.byId(post.getId()).get();
        assertEquals("New Title", updatedPost.getTitle());

//        var comment = new Comment();
//        comment.setContent("My Comment");
//        comment.setPost(post);
//        startTx();
//        blogger.insert(comment);
//        endTx();
//        assertNotNull(comment.getId());
    }


}

It failed with the following exception in console.

 BloggerTest.testBlogger » ArquillianProxy 
org.jboss.arquillian.junit5.IdentifiedTestException :
 null [Proxied because : Original exception caused: 
class java.lang.ClassNotFoundException: 
org.jboss.weld.exceptions.IllegalArgumentException]

The complete Glassfish server logs, check here.

@hantsy
Copy link
Author

hantsy commented Jul 14, 2024

I found this message in slack Jakarta EE/data channel.
https://eclipsefoundationhq.slack.com/archives/C040AJ787PU/p1715951162858049?thread_ts=1715902886.424969&cid=C040AJ787PU

That means EclipseLink will not provide the Jakarta Data implementations?

@OndroMih
Copy link
Contributor

Correct, @hantsy. I'm not a commiter on Eclipselink but I didn't see any signs that the Eclipselink project is going to implement Jakarta Data. We currently don't have a clear solution for GlassFish. I'm experimenting with integrating Eclipselink to the JNoSQL implementation of Jakarta Data, I have a good progress, but it's still not clear if it's a feasible way. For 2 main reasons - JNoSQL primarily focuses on Jakarta NoSQL, and even the NoSQL implementation of Jakarta Data is not complete yet. If we don't hit any showstopper with integrating Eclipselink to the JNoSQL solution, and if the JNoSQL project implements all Jakarta Data functionality, then we'll likely have a solution for GlassFish and even any app server. So far, my integration code does not depend on any Eclipselink-specific features and should support Hibernate or any other JPA implementation.

@hantsy
Copy link
Author

hantsy commented Jul 24, 2024

So far, my integration code does not depend on any Eclipselink-specific features and should support Hibernate or any other JPA implementation.

This is great. I hope it will be integrated into the next Glassfish milestone.

And your work will be a new subproject under Eclipse EE4j?

@hantsy
Copy link
Author

hantsy commented Jul 24, 2024

From other messages from Open Liberty, they have done the Jakarta Data implementation that is based on EclipseLink, why not port it back to EclipseLink project?

@OndroMih
Copy link
Contributor

OndroMih commented Jul 24, 2024

The OpenLiberty team say that their implementation is tightly coupled to OprnLiberty internals and thus it’s not easy to extract it.

My implementation should be in the JNoSQL project, which is already an Eclipse foundation project. I need some more time to prepare a PR to contribute what I have.

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

No branches or pull requests

2 participants