Spring JPA Repository is unable to create mapping tables of my entities - spring-data

I'm using spring data with H2 database and it's not creating database tables for my entity objects.
application.properties
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.url=jdbc:h2:file:~/app_db
spring.datasource.username=sa
spring.datasource.password=sa
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create
DomainApplication.java
#SpringBootApplication
#Import(DefaultPersistenceConfig.class)
#EntityScan(basePackages = {"com.sample.Domain.modal"})
public class DomainApplication {
public static void main(String[] args) {
SpringApplication.run(DomainApplication.class, args);
}
}
DefaultPersistanceConfig:
#Configuration
#EnableJpaRepositories(basePackages = {"com.sample.Domain.model" }, entityManagerFactoryRef = "defaultEntityManagerFactory", transactionManagerRef = "defaultTransactionManager")
#EnableTransactionManagement
public class DefaultPersistenceConfig {
#Autowired
public DataSource defaultDataSource;
#Bean
#Primary
#Qualifier("defaultEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean defaultEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(defaultDataSource).persistenceUnit("hib-unit")
.packages("com.sample.Domain.model").build();
}
#Bean
#Primary
#Qualifier("defaultTransactionManager")
public PlatformTransactionManager defaultTransactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
Entity class:
#Entity
#Table(name = "GENERAL_LEDGER_TRANSACTION")
public class GeneralLedgerTransaction implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id = 0L;
....
JPA Repository interface:
#Repository
public interface GeneralLedgerTransactionRepository extends JpaRepository<GeneralLedgerTransaction, Long> {
}
When I run the application and make a rest call to the controller to fetch data from the repository, it should return an empty array but I keep getting an error below.
org.h2.jdbc.JdbcSQLException: Table "GENERAL_LEDGER_TRANSACTION" not found; SQL statement:
select ... from GENERAL_LEDGER_TRANSACTION generalled0_ [42102-197]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:357) ~[h2-1.4.197.jar:1.4.197] ...

add spring.datasource.initialize=true in your application.properties

Related

Spring batch RepositoryItemWriter not working

I have a scenario where a given json file Employee.json needs to be read to Employee object and then written to employee table in mysql database in a batch process.
Employee.json:
[
{
"firstName": "John",
"lastName":"Snow",
"age":30
},
{
"firstName": "Tony",
"lastName": "Stark",
"age": 40
},
{
"firstName": "Peter",
"lastName":"Parker",
"age":35
}
]
Employee.java :
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Data
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private int age;
}
I used the JsonItemReader for reading the file from my resource location and RepositoryItemWriter for persisting the employee object.
This is how my batch config looks:
#Configuration
#ComponentScan(basePackages = "za.co.sanlam.employee")
#EnableBatchProcessing
#EnableTransactionManagement
public class DataInsertJsontoMysqlJob {
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Autowired
PlatformTransactionManager platformTransactionManager;
#Autowired
LocalContainerEntityManagerFactoryBean entityManagerFactory;
#Value("input/Employee.json")
private Resource inputjson;
#Bean(name = "insertDataJob")
public Job job(#Qualifier("step1") Step step1) {
return jobBuilderFactory.get("insertDataJob").incrementer(new RunIdIncrementer()).start(step1).build();
}
#Bean
protected Step step1(ItemWriter writer) throws ParseException {
return stepBuilderFactory
.get("step1")
.transactionManager(platformTransactionManager)
.<Employee, Employee>chunk(2)
.reader(jsonItemReader())
.writer(writer)
.build();
}
#Bean
public JsonItemReader<Employee> jsonItemReader() {
return new JsonItemReaderBuilder<Employee>()
.jsonObjectReader(new JacksonJsonObjectReader<>(Employee.class))
.resource(inputjson)
.name("jsonItemReader")
.build();
}
#Bean
public RepositoryItemWriter<Employee> writer(EmployeeRepository repository) {
final RepositoryItemWriter<Employee> repositoryItemWriter = new RepositoryItemWriter<>();
repositoryItemWriter.setRepository(repository);
repositoryItemWriter.setMethodName("save");
return repositoryItemWriter;
}
}
Even though the item writer code is executed no data is getting persisted to the Employee table on my local mysql database.
The application is able to connect to the database successfully and all the spring batch related tables are getting populated.
I was able to write the data to the Employee table if i use JdbcBatchItemWriter instead of RepositoryItemWriter:
#Bean //WORKING
public JdbcBatchItemWriter<Employee> writer(DataSource dataSource) {
JdbcBatchItemWriter<Employee> itemWriter = new JdbcBatchItemWriter<>();
itemWriter.setDataSource(dataSource);
itemWriter.setSql("insert into Employee (firstName,lastName,age) values (:firstName,:lastName,:age)");
itemWriter.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider());
itemWriter.afterPropertiesSet();
return itemWriter;
}
I am not including my databaseconfiguration class here.
What am i missing while using RepositoryItemWriter ?
I guess EmployeeRepository uses a JPA-based implementation. If this is the case, the cause of the issue would be that the platformTransactionManager injected on your step is of type DataSourceTransactionManager while it should be JpaTransactionManager.
Make sure you are using a JpaTransactionManager on your step and job repository when using RepositoryItemWriter.

Why can't Testcontainers find a table created with an init script?

I try to access a table in a PostgreSQLContainer created by an init script during startup but Testcontainers says that he can't find the table. Why???
#Entity
#Table(name = "named_person")
#Data
public class NamedPerson {
private static final long serialVersionUID = 8799954029578723024L;
#Column(name = "xxx")
private String xxx;
}
The test's base class:
#ActiveProfiles("test")
#Testcontainers
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class AbstractApplicationIT {
final static DockerImageName POSTGRES_IMAGE = DockerImageName.parse("postgres:13.2-alpine");
#Container
public static PostgreSQLContainer<?> postgreSQLContainer = new PostgreSQLContainer<>(POSTGRES_IMAGE);
#Test
public void contextLoads() {
}
}
My test class:
#Transactional
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class NamedPersonIT extends AbstractApplicationIT {
#Value("${spring.datasource.password}")
private String password;
#Value("${spring.datasource.username}")
private String username;
#Value("${spring.datasource.dbname}")
private String dbName;
#Value("${spring.datasource.initScript}")
private String initScript;
#Autowired
private NamedPersonRepository npr;
#BeforeAll
public void setup() {
postgreSQLContainer = new PostgreSQLContainer<>(POSTGRES_IMAGE)
.withDatabaseName(dbName)
.withUsername(username)
.withPassword(password)
.withInitScript(initScript);
postgreSQLContainer.start();
}
#Test
public void checkDbContainerIsAlive() {
assertThat(this.npr.findAll()).isNotNull();
}
}
The error message:
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Tabelle "PERSON" nicht gefunden
Table "PERSON" not found; SQL statement:
select namedpe0_.id as id1_2_, namedpe0_.xxx as xxx2_2_ from person namedpe0_ [42102-200]
The initScript:
DROP TABLE IF EXISTS named_person;
CREATE TABLE named_person (
id VARCHAR(36) PRIMARY KEY,
xxx VARCHAR(12)
);
INSERT INTO person VALUES ('1', 'abcdefg');

how to connect postgres in gradle project

I try to connect postgres in my project, here is my configuration
application.properties
spring.jpa.show-sql = true
spring.datasource.username = postgres
spring.datasource.password = password
spring.datasource.driverClassName = org.postgresql.Driver
spring.datasource.url = jdbc:postgresql://localhost:5432/postgres
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
Person.java
#Entity
#Table
#Data
public class Person {
#Id
#NotNull
private int id;
#NotNull
private String name;
public Person(int id, String name){
this.setId(id);
this.setName(name);
}
}
PersonRepository.java
#Repository
public interface PersonRepository extends JpaRepository<Person, Integer> {
}
PersonService.java
#Service
public class PersonService {
#Autowired
private PersonRepository personRepository;
public Person create(int id, String name){return personRepository.save(new Person(id, name));}
}
controller
#RestController
#SpringBootApplication
public class PostgresdemoApplication {
public static void main(String[] args) {
SpringApplication.run(PostgresdemoApplication.class, args);
}
#GetMapping("/hello")
public String hello(#RequestParam(value = "name", defaultValue = "world") String name){
return String.format("Hello, %s", name);
}
#GetMapping("/create")
public Person create(#RequestParam int id, #RequestParam String name){
return new PersonService().create(id, name);
}
}
when I run the spring application and enter http://localhost:8080/create?id=7&name=name
there is an exception
java.lang.NullPointerException: null
at com.example.postgresdemo.PersonService.create(PersonService.java:12) ~[main/:na]
at com.example.postgresdemo.PostgresdemoApplication.create(PostgresdemoApplication.java:24) ~[main/:na]
I've searched a lot and could not find where the problem lies. what can I do?
try to make it like this
#RestController
#SpringBootApplication
public class PostgresdemoApplication {
#Autowired
private PersonService personService;
public static void main(String[] args) {
SpringApplication.run(PostgresdemoApplication.class, args);
}
#GetMapping("/hello")
public String hello(#RequestParam(value = "name", defaultValue = "world") String name){
return String.format("Hello, %s", name);
}
#GetMapping("/create")
public Person create(#RequestParam int id, #RequestParam String name){
return new personService.create(id, name);
}
}

JPA generates negative sequence values generated for ID

I am creating a CRUD web application using JPA.
Technically every thing is working fine (no errors at all), but when checking my database I noticed that the after adding a new entry to my table, the ID generated from a sequence is a negative value: -46, -45, -44, etc ...
Here are the relevant parts of my code :
My entity :
#Entity
#NamedQuery(name="Book.findAll", query="SELECT b FROM Book b")
#SequenceGenerator(name="ma_seq", sequenceName="book_seq")
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ma_seq")
#Id private long id;
private String auteur;
private String langue;
private String titre;
public Book() {
}
//...getters and setters
}
My DAO :
public class MyDAO {
//Constructeur
public MyDAO(){
}
#PersistenceContext
private EntityManager em;
#Resource
private UserTransaction userTransaction;
public EntityManager getEm() {
return em;
}
public void setEm(EntityManager em) {
this.em = em;
}
#Transactional
public void register(Book livre) throws NotSupportedException, SystemException, SecurityException, IllegalStateException, RollbackException, HeuristicMixedException, HeuristicRollbackException {
// Save employee
userTransaction.begin();
this.em.persist(livre);
userTransaction.commit();
}
//other fonctions
}
after adding this to #SequenceGenerator then working fine!!!
allocationSize = 1

Why entityManager.contains returns different results?

This is in JPA2 (EclipseLink) and JSF2.
I have an entity class Student:
#Entity
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstname;
private String lastname;
private int age;
public Student(String firstname, String lastname, int age) {
this.firstname = firstname;
this.lastname = lastname;
this.age = age;
}
public Student() {
}
// accessors and mutators here
}
Session bean StudentFacade that inherits AbstractFacade:
public abstract class AbstractFacade<T> {
private Class<T> entityClass;
public AbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
public T edit(T entity) {
return getEntityManager().merge(entity);
}
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
// other methods: findAll, findRange, count
}
#Stateless
public class StudentFacade extends AbstractFacade<Student> {
#PersistenceContext(unitName = "jpa2testsPU")
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
public StudentFacade() {
super(Student.class);
}
public boolean contains(Student s) {
return getEntityManager().contains(s);
}
public void testContains() {
Student s = find(1L);
boolean isContains = getEntityManager().contains(s);
}
}
This is my JSF Managed Bean:
#ManagedBean
#RequestScoped
public class IndexController {
#EJB
private StudentFacade studentFacade;
/**
* Creates a new instance of IndexController
*/
public IndexController() {
}
public String test() {
Student s = new Student("John", "Doe", 20);
studentFacade.create(s);
Student s1 = studentFacade.find(1L); // This works because table only has 1 record
boolean isContains = studentFacade.contains(s);
return null;
}
}
When I run test() from managed bean, isContains is false. But when testContains() in StudentFacade is called, isContains is true. Why is this?
StudentFacade is a Stateless Session Bean (SSB). The contents of its instance variables are not guaranteed to be preserved across method calls (reference). It's like having a different instance of EntityManager created for each method invocation.
When you run your test from the managed bean, you invoke two different methods on the SSB, therefore a different EntityManager instance is created for each call, and the second one does not contain the Student instance because it has not been loaded yet.
But when you run your test inside a method of the SSB itself, the same EntityManager is used for the scope of the entire method, therefore the call to contains() returns true.