diff --git a/framework/hibernate-mysql/README.adoc b/framework/hibernate-mysql/README.adoc new file mode 100644 index 0000000..db63555 --- /dev/null +++ b/framework/hibernate-mysql/README.adoc @@ -0,0 +1 @@ +Tests if Hibernate with MySQL & HikariCP is working. diff --git a/framework/hibernate-mysql/build.gradle b/framework/hibernate-mysql/build.gradle new file mode 100644 index 0000000..82e3e11 --- /dev/null +++ b/framework/hibernate-mysql/build.gradle @@ -0,0 +1,27 @@ +plugins { + id "java" + id "org.springframework.boot" + id "org.springframework.cr.smoke-test" +} + +dependencies { + implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)) + implementation("org.springframework.boot:spring-boot-starter-jdbc") + implementation("org.springframework:spring-orm") + implementation("jakarta.persistence:jakarta.persistence-api") + implementation("org.hibernate.orm:hibernate-core") + + implementation("org.crac:crac:$cracVersion") + implementation(project(":cr-listener")) + + runtimeOnly("com.mysql:mysql-connector-j") + + testImplementation("org.springframework.boot:spring-boot-starter-test") + + appTestImplementation(project(":cr-smoke-test-support")) + appTestImplementation("org.awaitility:awaitility:4.2.0") +} + +crSmokeTest { + webApplication = false +} diff --git a/framework/hibernate-mysql/docker-compose.yml b/framework/hibernate-mysql/docker-compose.yml new file mode 100644 index 0000000..56371d7 --- /dev/null +++ b/framework/hibernate-mysql/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3.1' +services: + mysql: + image: 'mysql/mysql-server:8.0' + environment: + - 'MYSQL_DATABASE=test' + - 'MYSQL_USER=mysql' + - 'MYSQL_PASSWORD=password' + ports: + - '3306' diff --git a/framework/hibernate-mysql/src/appTest/java/com/example/hibernate/HibernateApplicationTests.java b/framework/hibernate-mysql/src/appTest/java/com/example/hibernate/HibernateApplicationTests.java new file mode 100644 index 0000000..a0b621a --- /dev/null +++ b/framework/hibernate-mysql/src/appTest/java/com/example/hibernate/HibernateApplicationTests.java @@ -0,0 +1,38 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.hibernate; + +import static org.assertj.core.api.Assertions.*; + +import java.time.Duration; + +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Test; +import org.springframework.cr.smoketest.support.assertj.AssertableOutput; +import org.springframework.cr.smoketest.support.junit.ApplicationTest; + +@ApplicationTest +public class HibernateApplicationTests { + + @Test + void connectionTest(AssertableOutput output) { + Awaitility.await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> { + assertThat(output).hasLineMatching("Starting DataReader: was (INITIALIZED|STOPPED)"); + assertThat(output).hasLineMatching("count \\d+: Author\\{id=\\d, name='.*'}"); + }); + } + +} diff --git a/framework/hibernate-mysql/src/main/java/com/example/hibernate/Author.java b/framework/hibernate-mysql/src/main/java/com/example/hibernate/Author.java new file mode 100644 index 0000000..df3fac8 --- /dev/null +++ b/framework/hibernate-mysql/src/main/java/com/example/hibernate/Author.java @@ -0,0 +1,79 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.hibernate; + +import java.util.Objects; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +@Entity +public class Author { + + @Id + @GeneratedValue + private Integer id; + + private String name; + + protected Author() { + } + + public Author(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Author author = (Author) o; + return Objects.equals(id, author.id) && Objects.equals(name, author.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + + @Override + public String toString() { + return "Author{" + "id=" + id + ", name='" + name + '\'' + '}'; + } + +} diff --git a/framework/hibernate-mysql/src/main/java/com/example/hibernate/DataReader.java b/framework/hibernate-mysql/src/main/java/com/example/hibernate/DataReader.java new file mode 100644 index 0000000..602e922 --- /dev/null +++ b/framework/hibernate-mysql/src/main/java/com/example/hibernate/DataReader.java @@ -0,0 +1,121 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.hibernate; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.transaction.Transactional; +import org.hibernate.Session; +import org.springframework.context.SmartLifecycle; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +@Component +@Transactional +@EnableScheduling +class DataReader implements SmartLifecycle { + + private final Object lifecycleMonitor = new Object(); + + private final AtomicInteger counter = new AtomicInteger(0); + + private State state = State.INITIALIZED; + + private List authorIds; + + @PersistenceContext + private EntityManager entityManager; + + enum State { + + INITIALIZED, STARTED, STOPPED + + } + + @Override + public void start() { + + synchronized (lifecycleMonitor) { + switch (state) { + case INITIALIZED, STOPPED -> { + System.out.printf("Starting DataReader: was %s\n", state); + if (ObjectUtils.isEmpty(authorIds)) { + authorIds = insertAuthors().stream().map(Author::getId).collect(Collectors.toList()); + } + state = State.STARTED; + } + } + } + } + + @Scheduled(fixedDelay = 1000) + public void scheduled() { + + if (isRunning()) { + + int count = counter.incrementAndGet(); + Author author = entityManager.find(Author.class, authorIds.get(count % 3)); + System.out.printf("count %03d: %s\n", count, author); + } + } + + @Override + public void stop() { + + synchronized (lifecycleMonitor) { + switch (state) { + case INITIALIZED, STARTED -> { + System.out.printf("Stopping DataReader: was %s\n", state); + state = State.STOPPED; + } + } + } + } + + @Override + public boolean isRunning() { + return State.STARTED.equals(state); + } + + private List insertAuthors() { + + Author brandonSanderson = new Author(null, "Brandon Sanderson"); + Author brentWeeks = new Author(null, "Brent Weeks"); + Author peterVBrett = new Author(null, "Peter V. Brett"); + + entityManager.persist(brandonSanderson); + entityManager.persist(brentWeeks); + entityManager.persist(peterVBrett); + + entityManager.flush(); + entityManager.getEntityManagerFactory().getCache().evictAll(); + + Session session = entityManager.unwrap(Session.class); + + if (session != null) { + session.clear(); // internal cache clear + } + + return List.of(brandonSanderson, brentWeeks, peterVBrett); + } + +} diff --git a/framework/hibernate-mysql/src/main/java/com/example/hibernate/HibernateApplication.java b/framework/hibernate-mysql/src/main/java/com/example/hibernate/HibernateApplication.java new file mode 100644 index 0000000..ded2b88 --- /dev/null +++ b/framework/hibernate-mysql/src/main/java/com/example/hibernate/HibernateApplication.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.hibernate; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@SpringBootApplication +@EnableTransactionManagement +public class HibernateApplication { + + public static void main(String[] args) throws InterruptedException { + SpringApplication.run(HibernateApplication.class, args); + } + +} diff --git a/framework/hibernate-mysql/src/main/resources/application.properties b/framework/hibernate-mysql/src/main/resources/application.properties new file mode 100644 index 0000000..7d5a815 --- /dev/null +++ b/framework/hibernate-mysql/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect +spring.jpa.hibernate.ddl-auto=create + +spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT_3306:3306}/test +spring.datasource.username=mysql +spring.datasource.password=password +spring.datasource.hikari.allow-pool-suspension=true diff --git a/framework/hibernate-mysql/src/test/java/com/example/hibernate/ApplicationTestes.java b/framework/hibernate-mysql/src/test/java/com/example/hibernate/ApplicationTestes.java new file mode 100644 index 0000000..5b239bc --- /dev/null +++ b/framework/hibernate-mysql/src/test/java/com/example/hibernate/ApplicationTestes.java @@ -0,0 +1,41 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.hibernate; + +import static org.assertj.core.api.Assertions.*; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@SpringBootTest +class ApplicationTestes { + + @PersistenceContext + EntityManager entityManager; + + @Test + void canQueryAuthors() { + + Query query = entityManager.createQuery("SELECT author.id, author.name FROM Author author"); + assertThat(query.getResultList()).isNotEmpty(); + } + +}