JPA not creating tables it found at startup - spring-boot

I'm new to JPA and am working through examples on a UDemy course and got confused with the various Spring initialization on startup options. My entity is not being created on application startup, though as an H2 in-memory database it should because it's not there.
My application-h2.properties:
spring.datasource.url=jdbc:h2:mem:ordersdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE
spring.datasource.username=ordersadmin
spring.datasource.password=secret
spring.jpa.database=mysql
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create-drop
#spring.datasource.defer-datasource-initialization=FALSE
#spring.jpa.defer-datasource-initialization=false
spring.sql.init.mode=NEVER
spring.flyway.user=ordersadmin
spring.flyway.password=secret
spring.flyway.locations=db/migration,db/specific/h2
spring.flyway.defaultSchema=orders
My application.properties:
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.type.descriptor.sql=trace
spring.h2.console.enabled=true
My entity class:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.Builder;
import lombok.Data;
#Entity
#Builder
#Data
public class OrderHeader {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;
private String customerName;
}
Ultimately, I'm trying to get the SQL for the table put into a Flyway configuration file, but prefer to start with JPA's generated SQL statement for this. I thought telling it
spring.jpa.hibernate.ddl-auto=create-drop would tell it to create or drop any tables it needed to, but it's not doing that.
My confusion comes from properties like:
spring.sql.init.mode
spring.datasource.defer-datasource-initialization
I believe these were related to non-Flyway/non-Liquibase type initialization driven by some schema.sql file, but can't find consistent docs on that.

Related

Entity not being loaded, or Mysterious "No property 'saveAll' found..." or "Could not safely identify store assignment..."

I want JPA to recognize an entity, and generate the SQL necessary to create the table. I can get it to fail in one of the following ways. Below I show the error, followed by the code for the entity. The behavior changed depending on which package was used for things like Table so I deliberately showed which one was being used below.
Error: RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface com.woodsman.jpatutorial.orders.repositories.OrderHeaderRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table.
OrderHeader.java:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
#Entity This should be simple, but I think there's several pack
#Builder
#Data
#NoArgsConstructor
#AllArgsConstructor This should be simple, but I think there's several pack
#EqualsAndHashCode
#Table(name = "order_header")
public class OrderHeader {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private String id; -->
private String customerName;
}
Error: Error creating bean with name 'orderHeaderRepository' defined in com.woodsman.jpatutorial.orders.repositories.OrderHeaderRepository defined in #EnableJdbcRepositories declared on JdbcRepositoriesRegistrar.EnableJdbcRepositoriesConfiguration: Could not create query for public abstract java.util.List org.springframework.data.jpa.repository.JpaRepository.saveAllAndFlush(java.lang.Iterable); Reason: No property 'saveAll' found for type 'OrderHeader'
OrderHeaderRepository.java:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.woodsman.jpatutorial.orders.entities.OrderHeader;
#Repository
public interface OrderHeaderRepository extends JpaRepository<OrderHeader,Long>{
}
OrderHeader.java
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
#Entity
#Builder
#Data
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
#org.springframework.data.relational.core.mapping.Table(name = "order_header",schema = "orders")
public class OrderHeader {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;
private String customerName;
}
application-h2.properties
spring.datasource.url=jdbc:h2:mem:ordersdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE
spring.datasource.username=ordersadmin
spring.datasource.password=secret
spring.jpa.database=mysql
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create-drop
database, ALWAYS initialize the database
spring.sql.init.mode=ALWAYS
spring.flyway.user=ordersadmin
spring.flyway.password=secret
spring.flyway.locations=db/migration,db/specific/h2
spring.flyway.defaultSchema=orders
application.properties:
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.type.descriptor.sql=trace
spring.h2.console.enabled=true
spring.jpa.show-sql=true
spring.data.jdbc.reposi -->
tories.enabled=true
Update: Switched back to Spring Boot 2.7.3 and things work. Using Spring Boot version 3.0.0+ apparently removes the EnableJpaRepositories annotation. Without that annotation, and with the 3.0.0+ Spring Boot version, Spring said the entity wasn't a managed type. Per #M.Deinum's comment, I commented out spring-boot-starter-data-jdbc and the code did run, but it wouldn't actually generate the create sql.
I'm convinced that this should work in the 3.0.0-M5 Spring Boot, albeit with somewhat substantial changes to dependencies, and perhaps new annotations being added, so I'm not considering my question answered. Also, I made the entity id type Long to be consistent with the repository.

JpaRepsotory custom query method executes but I can see no record updated in database table

I have the updatePassword interface which extends the JpaRepository -
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.erecruitment.app.model.user;
#Repository
public interface updatePassword extends JpaRepository<user, Long>{
public String nativeQuery="UPDATE user SET password=?1 WHERE username=?2";
#Transactional
#Modifying
#Query(value=nativeQuery,nativeQuery=true)
int updtPassword(String password,String username);
}
After application runs it give the following response in the console -
Hibernate:
UPDATE
user
SET
password=?
WHERE
username=?
But, the problem is when I check the database table the values are still same .Also ,there is no error from spring boot . Where am I going wrong?
That query looks good... maybe the problem somewhere else... Are you sure you connect ot the proper DB? (eg. You made some changes in the properties, or connect Spring connects to an autogenerated inmemory db, etc.)

Unable to get table autocreated in spring module integration test

I have a parameters module in my project which other modules are dependent on. I'd like to write instegration tests for this module in separate manner. This module is a spring based one and has some db related logic, though db migration scripts are not available in this module since it is something that is out of its area of resposibility. By the way in-memory H2 instance is used for testing purposes.
What I'm trying to achieve is to make spring/hibernate create DB tables based on single #Entity class present in this module, it is called ParameterEntity.
It is defined like this:
#Entity
#Table(name = "SYSTEM_PARAMETERS")
public class ParameterEntity {
#Id
#Column(name = "id")
private String id;
#Column(name = "value")
private String value;
// accessors go here...
}
In my application-test.yml file I provide the following props:
spring:
jpa:
hibernate:
ddl-auto: create
database: h2
show-sql: true
And define integration test class like this:
#ExtendWith(SpringExtension.class)
#EnableConfigurationProperties
#EntityScan("path.to.package.where.parameter.entity.resides")
#ConstextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
#ActiveProfiles("test")
#TestInstance(Lifecycle.PER_CLASS)
public class ParametersIntegrationTest {
#Autowired
private ParameterWarden warden;
#BeforeEach
public void setUp() {
warden.initialize();
}
#Configuration
#ComponentScan("base.module.package")
static class TestConfiguration {
// here comes some test beans which will be used in testing purposes only
}
}
In #BeforeEach method ParameterWarden class calls repository class which in turn make some calls to database to retrieve parameter entities from database and these calls fail because SYSTEM_PARAMETERS is missing.
Could anyone, please, let me know what am I missing here and how can I make spring or hibernate create table based on the entity present in my project. Even the place where I can debug this would be nice to know.
It seems like I need another magical thing that will trigger this functionality but I was unable to figure out what exactly I need.
Any help is really appreciated, thank you very much for your time!
p.s. I can not use #SpringBootTest annotation since this module uses only some spring features and is not a spring boot application itself. It is used as a dependecy in another spring boot applications.
Can you still use #DataJpaTest?
package com.test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
#DataJpaTest
#ContextConfiguration(classes = TestEntityRepository.class)
#TestPropertySource(properties = {
"spring.jpa.hibernate.ddl-auto=create",
"spring.datasource.platform=h2",
"spring.jpa.show-sql=true"
})
#EnableAutoConfiguration
public class IntegrationTest {
#Autowired
TestEntityRepository testEntityRepository;
#Test
void testCreateRead() {
var saved = testEntityRepository.save(new TestEntity("test"));
Assertions.assertNotNull(saved);
var read = testEntityRepository.findById(saved.getId());
Assertions.assertNotNull(read);
}
}
Where repository and entity in the com.test package are:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface TestEntityRepository extends CrudRepository<TestEntity, Long> { }
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
#Entity
#Data
#NoArgsConstructor
public class TestEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private Long id;
#Column
private String attr;
public TestEntity(String attr) {
this.attr = attr;
}
}
Dependecies used (through dependency management and Spring Boot BOM of version 2.3.4.RELEASE):
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
Alternatively, if you want to fully decouple tests from Spring you can use org.hibernate.tool.hbm2ddl.SchemaExport in some utility logic since that's what's really executing under the hood. I use this approach since my project requires some extra steps to setup the database, however, it might be too complicated for some use cases.

MongoRepository Delete Method

Good afternoon fellow coders!
I have spent the last hour looking to delete a single document from my mongo "testCollection". I would like to make use of the MongoRepository delete / deleteAll methods. However, it does not remove the document. It persists regardless of how many times I run the test class method. No errors are reported and the user has readWrite permissions in the database. I am able to run the mongo command to remove the newly created test document.
I have read about using the mongo template and create it for the deletion to be performed. I'm happy to do that but I would rather keep it as simple as possible.
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoId;
#Data
#Document(collection = "testCollection")
public class TestClass {
#MongoId
private String id;
private String name;
public TestClass(String name) {
this.name = name;
}
}
Test Class Mongo Repository interface
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface TestClassRepository extends MongoRepository<TestClass, String> {
public List<TestClass> findAllByName(String name);
public void deleteAllByIdIn(List<TestClass> list);
}
Test Method
import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
#RunWith(SpringRunner.class)
#TestPropertySource(value = {"classpath:application.properties"})
#AutoConfigureMockMvc
public class testClassTest {
#Autowired
private TestClassRepository testClassRepository;
#Test
public void crudTest() {
TestClass testObj = new TestClass("Test");
testClassRepository.save(testObj);
List<TestClass> testClassList = testClassRepository.findAllByName("Test");
Assert.assertEquals(1, testClassList.size());
TestClass test = testClassList.get(0);
testClassRepository.deleteAllByIdIn(testClassList);
// Fails this assertion: Found 1, expected 0.
Assert.assertEquals(0, testClassRepository.findAllByName("Test").size());
}
}
As anyone else experienced a similar issue? If so, how'd you go about resolving it?
Thanks!
Additions to original post:
Here is the mongo query generated by the MongoRepository. It appears that it is not actually adding the "remove" mongo command to the query. Query: { "name" : "Test"}, Fields: {}, Sort: {}
With a stroke of dumb luck I managed to figure out the issue. The problem was with the type of identifier annotation I was using. This explanation from another stackoverflow user (What is use of #MongoId in Spring Data MongoDB over #Id?) had me revisit this aspect of the model.
I switched the identifier annotation from #MongoId to #Id. Since I have both JPA and MongoDB annotations I needed to make sure I chose the one from the org.springframework.data.annotation package rather than the javax.persistance package.
Hope this explanation helps others!

Spring boot JPA repository committing code even if #Transactional placed in Service layer

See below spring boot code
I have used JPA repository.
Controller.
Service.
Repository
BaseController
package com.controller;
import com.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class BaseController {
#Autowired
private StudentService studentService;
#GetMapping(value = "/addStudent", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<String> base() {
studentService.save();
return new ResponseEntity<String>("SUCCESS", HttpStatus.OK);
}
}
StudentService.java
package com.service;
import com.model.Student;
import com.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
#Service("studentService")
public class StudentServiceImpl implements StudentService {
#Autowired
private StudentRepository studentRepository;
#Override
#Transactional
public Student save() {
Student student = new Student();
student.setFirstName("ABC");
student.setLastName("PQR");
studentRepository.save(student);
int i = 10 / 0; //Error code
return student;
}
}
StudentRepository
package com.repository;
import com.model.Student;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository("studentRepository")
public interface StudentRepository extends CrudRepository<Student, Long> {
public List<Student> findAll();
}
Application.properties
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
#maximum number of milliseconds that a client will wait for a connection
spring.datasource.hikari.connection-timeout = 20000
#minimum number of idle connections maintained by HikariCP in a connection pool
spring.datasource.hikari.minimum-idle= 10
#maximum pool size
spring.datasource.hikari.maximum-pool-size= 10
#maximum idle time for connection
spring.datasource.hikari.idle-timeout=10000
# maximum lifetime in milliseconds of a connection in the pool after it is closed.
spring.datasource.hikari.max-lifetime= 1000
#default auto-commit behavior.
spring.datasource.hikari.auto-commit =false
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.jdbcUrl=jdbc:mysql://localhost:3306/demo?autoReconnect=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.show-sql=true
spring.jpa.properties..hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.ddl-auto=update
After executing save method from StudentRepository, data get inserted
immediately into database. no rollback or any other isolation levels are
working in StudentServiceImpl.java even if Error code is there.
I have tried to set "spring.datasource.hikari.auto-commit =true" setting value true, Placed #Transaction at top of the StudentServiceImpl.java class but still it didn't worked.
You do not need to mess with any hikari settings, and certainly not with autoCommit(true) as this DISABLES transactions. Delete all these properties.
Where is the "error" in your code? Spring rolls back on unchecked exceptions being thrown (not checked ones or errors), I cannot see that in your code.
What behavior do you expect? It looks fine to me.
It's probably beacause of Open Jpa in View behaviour.
Write the following line in your properties file:
spring.jpa.open-in-view=false
take a look at this if you want to know more.

Resources