package com.vladmihalcea.hibernate.masterclass.laboratory.cache;

import com.vladmihalcea.hibernate.masterclass.laboratory.util.AbstractIntegrationTest;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.junit.Before;
import org.junit.Test;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

/**
 * SecondLevelCacheTest - Test to check the 2nd level cache
 *
 * @author Vlad Mihalcea
 */
public class SecondLevelCacheTest extends AbstractIntegrationTest {

    @Override
    protected Class<?>[] entities() {
        return new Class<?>[]{
                Post.class,
                PostDetails.class,
                Comment.class
        };
    }

    @Override
    protected Properties getProperties() {
        Properties properties = super.getProperties();
        properties.put("hibernate.cache.use_second_level_cache", Boolean.TRUE.toString());
        properties.put("hibernate.cache.use_query_cache", Boolean.TRUE.toString());
        properties.put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory");
        return properties;
    }

    @Before
    public void init() {
        super.init();
        doInTransaction(session -> {
            Post post = new Post();
            post.setName("Hibernate Master Class");

            post.addDetails(new PostDetails());
            post.addComment(new Comment("Good post!"));
            post.addComment(new Comment("Nice post!"));

            session.persist(post);
        });
    }

    @Test
    public void test2ndLevelCache() throws InterruptedException {
        LOGGER.info("Test 2nd level cache");
        doInTransaction(session -> {
            /*Post post = (Post) session.createQuery(
                    "select p " +
                            "from Post p " +
                            "join fetch p.details " +
                            "where " +
                            "   p.id = :id"
            ).setParameter("id", 1L)
            .uniqueResult();*/
            Post post = (Post) session.get(Post.class, 1L);
        });
        doInTransaction(session -> {
            LOGGER.info("Check entity is cached");
            Post post = (Post) session.get(Post.class, 1L);
        });
    }

    @Entity(name = "Post")
    @Cacheable
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public static class Post {

        @Id
        @GeneratedValue(strategy=GenerationType.AUTO)
        private Long id;

        private String name;

        @OneToMany(cascade = CascadeType.ALL, mappedBy = "post", orphanRemoval = true)
        @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
        private List<Comment> comments = new ArrayList<>();

        @OneToOne(cascade = CascadeType.ALL, mappedBy = "post", optional = false, fetch = FetchType.LAZY)
        @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
        private PostDetails details;

        public String getName() {
            return name;
        }

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

        public List<Comment> getComments() {
            return comments;
        }

        public PostDetails getDetails() {
            return details;
        }

        public void addComment(Comment comment) {
            comments.add(comment);
            comment.setPost(this);
        }

        public void addDetails(PostDetails details) {
            this.details = details;
            details.setPost(this);
        }
    }

    @Entity(name = "PostDetails")
    @Cacheable
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public static class PostDetails {

        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;

        private Date createdOn;

        public PostDetails() {
            createdOn = new Date();
        }

        @OneToOne(fetch = FetchType.LAZY)
        @PrimaryKeyJoinColumn
        private Post post;

        public Long getId() {
            return id;
        }

        public void setPost(Post post) {
            this.post = post;
        }
    }

    @Entity(name = "Comment")
    @Cacheable
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public static class Comment {

        @Id
        @GeneratedValue(strategy=GenerationType.AUTO)
        private Long id;

        @ManyToOne
        private Post post;

        public Comment() {}

        public Comment(String review) {
            this.review = review;
        }

        private String review;

        public Long getId() {
            return id;
        }

        public void setPost(Post post) {
            this.post = post;
        }

        public void setReview(String review) {
            this.review = review;
        }
    }
}

@@ -1629,38 +1629,17 @@
 ache
-() throws InterruptedException
+WithGet()
  %7B%0A 
@@ -1731,18 +1731,405 @@
         
-/*
+Post post = (Post) session.get(Post.class, 1L);%0A        %7D);%0A        doInTransaction(session -%3E %7B%0A            LOGGER.info(%22Check entity is cached after load%22);%0A            Post post = (Post) session.get(Post.class, 1L);%0A        %7D);%0A    %7D%0A%0A    @Test%0A    public void test2ndLevelCacheWithQuery() %7B%0A        LOGGER.info(%22Test 2nd level cache%22);%0A        doInTransaction(session -%3E %7B%0A            
 Post pos
@@ -2375,29 +2375,16 @@
 d = :id%22
-%0A            
 ).setPar
@@ -2416,26 +2416,188 @@
     
-.uniqueResult();*/
+        .setCacheable(true)%0A                    .uniqueResult();%0A        %7D);%0A        doInTransaction(session -%3E %7B%0A            LOGGER.info(%22Check get entity is cached after query%22);
 %0A   
@@ -2657,32 +2657,33 @@
 L);%0A        %7D);%0A
+%0A
         doInTran
@@ -2726,32 +2726,38 @@
 GER.info(%22Check 
+query 
 entity is cached
@@ -2756,16 +2756,28 @@
 s cached
+ after query
 %22);%0A    
@@ -2811,34 +2811,343 @@
 session.
-get(Post.class, 1L
+createQuery(%0A                    %22select p %22 +%0A                            %22from Post p %22 +%0A                            %22join fetch p.details %22 +%0A                            %22where %22 +%0A                            %22   p.id = :id%22).setParameter(%22id%22, 1L)%0A                    .setCacheable(true)%0A                    .uniqueResult(
 );%0A     
@@ -3362,33 +3362,35 @@
 edValue(strategy
-=
+ = 
 GenerationType.A
@@ -5380,17 +5380,19 @@
 strategy
-=
+ = 
 Generati
@@ -5503,16 +5503,25 @@
 ment() %7B
+%0A        
 %7D%0A%0A     

Add query cache example too