Spring boot liquibase. Execute DIFF between entities and database - spring

I am trying to set up liquibase in my spring boot application. What I am trying is to create change sets between my Entities and my DB with mvn liquibase:diff
Here is example of one of my entities:
package com.example.start.project.entity;
import static com.example.start.project.entity.DatabaseConstants.CREATED_AT;
import static com.example.start.project.entity.DatabaseConstants.CREATED_BY;
import static com.example.start.project.entity.DatabaseConstants.LEGACY_ID;
import static com.example.start.project.entity.DatabaseConstants.MODIFIED_AT;
import static com.example.start.project.entity.DatabaseConstants.MODIFIED_BY;
import static com.example.start.project.entity.DatabaseConstants.SCHEMA_NAME;
import static com.example.start.project.entity.DatabaseConstants.TABLE_PREFIX;
import static com.example.start.project.entity.PlantEntity.TSN_PREFIX;
import java.time.ZonedDateTime;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.springframework.data.annotation.Immutable;
#Entity
#Table(schema = SCHEMA_NAME, name = TABLE_PREFIX + TSN_PREFIX + "PLANT")
#Immutable
public class PlantEntity {
/** Table short name. */
static final String TSN = "PLNT";
/** Table short name as prefix. */
static final String TSN_PREFIX = TSN + "_";
/** Id column name */
static final String ID_COLUMN = TSN_PREFIX + DatabaseConstants.ID;
private static final long serialVersionUID = 1L;
#Id
#Column(name = ID_COLUMN)
private UUID id;
#Column(name = TSN_PREFIX + LEGACY_ID)
private Long legacyId;
#Column(name = TSN_PREFIX + "NUMBER")
private String number;
#Column(name = TSN_PREFIX + "NAME")
private String name;
#Column(name = TSN_PREFIX + "TYPE")
private String type;
#Column(name = TSN_PREFIX + CREATED_BY)
private String createdBy;
#Column(name = TSN_PREFIX + CREATED_AT)
private ZonedDateTime createdAt;
#Column(name = TSN_PREFIX + MODIFIED_BY)
private String modifiedBy;
#Column(name = TSN_PREFIX + MODIFIED_AT)
private ZonedDateTime modifiedAt;
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public Long getLegacyId() {
return legacyId;
}
public void setLegacyId(Long legacyId) {
this.legacyId = legacyId;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public ZonedDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(ZonedDateTime createdAt) {
this.createdAt = createdAt;
}
public String getModifiedBy() {
return modifiedBy;
}
public void setModifiedBy(String modifiedBy) {
this.modifiedBy = modifiedBy;
}
public ZonedDateTime getModifiedAt() {
return modifiedAt;
}
public void setModifiedAt(ZonedDateTime modifiedAt) {
this.modifiedAt = modifiedAt;
}
}
And here is my POM looks like:
*******
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>3.8.8</version>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-cdi</artifactId>
<version>3.8.8</version>
</dependency>
<dependency>
<groupId>org.liquibase.ext</groupId>
<artifactId>liquibase-hibernate5</artifactId>
<version>3.8</version>
</dependency>
</dependencies>
</dependencyManagement>
*********
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>3.8.8</version>
<configuration>
<propertyFile>liquibase/liquibase.properties</propertyFile>
<changeLogFile>liquibase/changeLogs/database.yaml</changeLogFile>
<driver>${test.jdbc.driver}</driver>
<url>${test.jdbc.url}?currentSchema=${test.jdbc.schema}</url>
<username>${test.jdbc.userName}</username>
<password>${test.jdbc.password}</password>
<!--defaultSchemaName>${test.jdbc.schema}</defaultSchemaName>-->
<changelogSchemaName>${test.jdbc.schema}</changelogSchemaName>
<referenceDriver>liquibase.ext.hibernate.database.connection.HibernateDriver</referenceDriver>
<referenceUrl>hibernate:spring:com.example.start.project.entity?dialect=org.hibernate.dialect.PostgreSQL95Dialect</referenceUrl>
<diffChangeLogFile>${project.build.directory}/startup.db-diff.yaml</diffChangeLogFile>
<outputChangeLogFile>${project.build.directory}/db-initial.yaml</outputChangeLogFile>
<!-- Syntax: [objecttype from liquibase.structure.core]:[regex matching name] -->
<diffExcludeObjects>sequence:.*_SEQ</diffExcludeObjects>
<promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
<logging>debug</logging>
</configuration>
<executions>
<execution>
<phase>process-resources</phase>
<goals>
<goal>update</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>${test.jdbc.groupId}</groupId>
<artifactId>${test.jdbc.artifactId}</artifactId>
<version>${test.jdbc.version}</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${java-validation-api.version}</version>
</dependency>
<dependency>
<groupId>org.liquibase.ext</groupId>
<artifactId>liquibase-hibernate5</artifactId>
<version>${liquibase.hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</pluginManagement>
</build>
When I run mvn liquibase:diff i got No changesets to add. even I have few entities and my DB is empty. DB is Postgress.
I think that issue is with referenceUrl:
<referenceUrl>hibernate:spring:com.example.start.project.entity?dialect=org.hibernate.dialect.PostgreSQL95Dialect</referenceUrl>
and that liquibase is not able to find my entities.
Entities are placed in different maven module than from where I am running mvn liquibase:diff but this module has reference to it.
Does anyone see what I am doing wrong. Thanks in advance.

You can add this liquibase plugin with a path to liquibase properties file:
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<propertyFile>src/main/resources/liquibase.properties</propertyFile>
</configuration>
</plugin>
And liquibase.properties should look like this:
url=jdbc:oracle:thin:#1.1.1.1:1521:ORCL
username=name
password=pas
driver=oracle.jdbc.driver.OracleDriver
outputChangeLogFile=src/main/resources/liquibase-outputChangeLog.xml
changeLogFile=classpath:/db/changelog/db.changelog-master.xml
referenceUrl=hibernate:spring:com.your.model?dialect=org.hibernate.dialect.Oracle12cDialect
diffChangeLogFile=target/db.changelog-diff.xml
Then: mvn liquibase:diff

Related

Mapstruct Implementation

I am using mapstruct to map my model to my DTO.
I want to search for a record by the full name.
I do not understand why I get the following errors:
Error creating bean with name 'customerController'
Error creating bean with name 'customerServiceImpl'
Error creating bean with name 'customerRepository'
No property name found for type Customer!
this is my project
public interface CustomerMapper {
CustomerMapper INSTANCE = Mappers.getMapper(CustomerMapper.class);
#Mapping(source = "lastName", target = "lastName")
CustomerDTO customerToCustomerDTO(Customer customer);
}
#Data
public class CustomerDTO {
private String firstName;
private String lastName;
}
#Data
#Entity
#Getter
#Setter
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String name;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
public class CustomerListDTO {
List<CustomerDTO> categories;
}
#Controller
#RequestMapping("api/v1/customers")
public class CustomerController {
private final CustomerService customerService;
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
#GetMapping("{name}")
public ResponseEntity<CustomerDTO> getCustomerByName(#PathVariable String name) {
return new ResponseEntity<>(
customerService.getCustomerByName(name), HttpStatus.OK
);
}
public interface CustomerRepository extends JpaRepository<Customer, Long> {
Customer findByName(String x);
}
public interface CustomerService {
CustomerDTO getCustomerByName(String name);
}
#AllArgsConstructor
#Service
public class CustomerServiceImpl implements CustomerService {
CustomerMapper customerMapper;
CustomerRepository customerRepository;
#Override
public CustomerDTO getCustomerByName(String lastName) {
return customerMapper.customerToCustomerDTO(customerRepository.findByName(lastName));
}
}
This is a potential fix: would be to map the below in the CustomerMapper, but to me it doesn't feel right.
#Mapping(source = "name", target = "lastName")
#Mapping(source = "firstName", target = "firstName")
In the documentation, it is said that you can map whatever field from model to DTO, I think there might be something wrong in my code.
The way I try implementing in the repo, service, controller.
Edit:
Maybe a solution would be to use DTO in Repository?
Update:
#Override
public CustomerDTO getCustomerByName(String lastName) {
return customerRepository.findByName(lastName).map(customerMapper::customerToCustomerDTO);
}
.map cannot be used.
for .map to be used I should use code like this
.findAll()
.stream()
.map(customerMapper::customerToCustomerDTO)
.collect(Collectors.toList());
I am using the findByName method however, that doesn't have access to .map.
How can I solve that problem?
EDIT
this is how my Customer I think should look like
#Data
#NoArgsConstructor
#AllArgsConstructor
public class CustomerDTO {
private String id;
private String firstName;
private String lastName;
}
"No property name found for type Customer!"
In you table costumer you have a column with name "name"?
Below I made some changes in your code, however if you need to find by name your repository needs to find correct search. When you use findByName only return rows where name is equals to name passed in parameter. Example: findByName("Scilla") only return rows where column name is equals to "Scilla", if a column name have values like "scilla" (lower) or "Scilla abc" this entries was not returned by query.
Method findByName with value "Scilla" generate this query:
select * from customer where name = 'Scilla';
Code changes
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CustomerRepository extends JpaRepository<Customer, Long> {
Customer findByLastName(String lastName);
List<Customer> findByLastNameContainingIgnoreCase(String name);
List<Customer> findByLastNameContaining(String name);
}
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
#Mapper
public interface CustomerMapper {
CustomerMapper INSTANCE = Mappers.getMapper(CustomerMapper.class);
CustomerDTO customerToCustomerDTO(Customer customer);
Customer toDomain(CustomerDTO customerDTO);
}
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#Entity
#NoArgsConstructor
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
}
import lombok.Data;
#Data
public class CustomerDTO {
private String firstName;
private String lastName;
}
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("api/v1/customers")
public class CustomerController {
private final CustomerService customerService;
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
#GetMapping("{name}")
public ResponseEntity<CustomerDTO> getCustomerByName(#PathVariable String name) {
return new ResponseEntity<>(
customerService.getCustomerByName(name), HttpStatus.OK
);
}
#PostMapping
public ResponseEntity<CustomerDTO> getCustomerByName(#RequestBody CustomerDTO customerDTO ) {
return new ResponseEntity<>(
customerService.save(customerDTO), HttpStatus.OK
);
}
}
Important
Below put Spring Data query and translation query.
List<Customer> findByLastNameContainingIgnoreCase(String name)
select * from customer where last_name ilike = '%name%';
pom.xml definition
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
<org.mapstruct.version>1.4.1.Final</org.mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.1.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<forceJavacCompilerUse>true</forceJavacCompilerUse>
<release>11</release>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
To map your Customer entity to DTO and use it in the Spring application you should use the following mapper (with parameter componentModel = "spring"):
#Mapper(
componentModel = "spring",
nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT,
nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE
)
public interface CustomerMapper {
#Mapping(target = "lastName", source = "name")
CustomerDto toDto(Customer customer);
}
MapStruct doesn't know how to map name property in your entity to lastName property in the DTO, so you have to specify this in #Mapping annotation.
(I also recommend to use specified values of 'strategy' parameters - you can check their purpose in javadoc.)
In this case, MapStruct generates an appropriate Spring bean with the implementation of your mapper, something like this:
#Component
public class CustomerMapperImpl {
public CustomerDto toDto(Customer customer) {
CustomerDto dto = new CustomerDto();
if (customer != null) {
if (customer.getFirstName() != null) {
dto.setFirstName(customer.getFirstName());
}
if (customer.getName() != null) {
dto.setLastName(customer.getName());
}
}
return dto;
}
}
So Spring will be able to inject that bean in your service (don't forget to correct findByName method of your repo to return Optional):
#RequiredArgsConstructor
#Service
public class CustomerServiceImpl implement CustomerService {
private final CustomerRepo repo;
private final CustomerMapper mapper;
#Override
public Optional<CustomerDto> getByName(#NonNull String name) {
return repo.findByName(name).map(mapper::toDto)
}
#Override
public List<CustomerDto> getAll() {
return repo.findAll().stream().map(mapper::toDto).collect(Collectors.toList());
}
}
And then use this service in your REST controller:
#RequiredArgsConstructor
#RestController
#RequestMapping("api/v1/customers")
public class CustomerController {
private final CustomerService service;
#GetMapping("/{name}")
public CustomerDto getByName(#PathVariable String name) {
return service.getByName()
.orElseThrow(() -> new ResponseStatusException("Customer not found"));
}
#GetMapping
public List<CustomerDto> getAll() {
return service.getAll();
}
}
Don't forget to configure your project to use MapStruct and Lombok together:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

Mapstruct does not use builders defined by Lombok

Solution:
I had to change the ordering of my mapstruct and lombok annotationProcessorPaths.
I had to place mapstruct above lombok, then it worked.
I updated the pom below to the working version, so there is no non-working-code in here.
I also converted the lombok version back to the current release and not using the edge-version.
Original Problem:
I have 2 more or less identical sets of classes (see example below)
one set are the DTOs of my API, which I want to have immutable, using Lombok's #Value and #Builder
one set are the entities that are going to be stored in the database. With Lombok's #Data
Initially I set the project up to use:
Lombok 1.18.12
Mapstruct 1.3.1
Java 11
Maven
I found the Lombok documentation explaining how to add the annotation-processor to the maven-plugin
https://projectlombok.org/setup/maven
But when executing I still get Error:(16,25) java: ClassX does not have an accessible parameterless constructor.
Searching for this message I found some 2 to 3 years of problems, but nothing up to date. Also I saw, that the issue was resolved for those posts.
In at least one of the posts it was mentioned, that it worked, when splitting the project into modules. And this worked for me as well. When I move the DTOs to another maven module, build them there and set the dependency it works, but this is definitely not the project-structure I want to have. Also since I might need to move my entities out as well and I don't want to create a new module for each Pojo-structure I'm creating.
I also found that post on the Lombok Edge version:
https://projectlombok.org/download-edge
The second point in the change-list is
BREAKING CHANGE: mapstruct users should now add a dependency to lombok-mapstruct-binding. This solves compiling modules with lombok (and mapstruct).
So I tried that as well.
I added the repository to my pom, added lombok-mapstruct-binding and set the lombok version to edge-SNAPSHOT
But even after a clean the compile step fails.
In between I changed my DTOs to use #Data as well, but I would like to change this back.
Finally here are some examples and details on the code.
DTOs
#Data
#AllArgsConstructor(access = AccessLevel.PROTECTED)
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = BDto.class, name = "b"),
#JsonSubTypes.Type(value = CDto.class, name = "c")
})
public abstract class ADto {
private long id;
private String type;
private Set<String> metadata;
private Set<String> tags;
}
#Data
#NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BDto extends ADto {
private String path;
#Builder
private BDto(long id, String path, Set<String> metadata, Set<String> tags) {
super(id, "b", metadata, tags);
this.path = path;
}
}
#Data
#NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CDto extends ADto {
private String name;
private Set<A> collection;
#Builder
private CDto(long id, String name, Set<A> collection, Set<String> metadata, Set<String> tags) {
super(id, "c", metadata, tags);
this.collection = collection;
this.name = name;
}
}
Entities
#Entity
#Table
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type")
#AllArgsConstructor
#NoArgsConstructor
#Getter
public abstract class A extends PanacheEntityBase {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected long id;
#Column(name = "type", insertable = false, updatable = false)
private String type;
/* ... */
}
#Entity
#DiscriminatorValue("b")
#NoArgsConstructor
#Getter
#ToString
public class B extends A {
public B(long id, String path, Set<String> metadata, Set<Tag> tags) {
super(id, "b", metadata, tags);
this.path = path;
}
public B(String path) {
super(0, "b", new HashSet<>(), new HashSet<>());
this.path = path;
}
#Column(name = "path")
#Setter
private String path;
}
#Entity
#DiscriminatorValue("c")
#NoArgsConstructor
#Getter
public class C extends A {
public C(long id, String name, List<A> collection, Set<String> metadata, Set<Tag> tags) {
super(id, "c", metadata, tags);
this.collection = collection;
this.name = name;
}
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "c_id")
#OrderBy("order")
List<A> collection;
}
Mappers
public interface AMapper {
default String tagToDto(Tag tag) {
return tag.getTag();
}
default Tag tagFromDto(String tag) {
return Tag.createIfNotExists(tag);
}
}
#Mapper()
public interface BMapper extends AMapper {
#Override
#Mapping(target = "tags",
qualifiedByName = "tagToDto")
BDto toDto(B b);
#Override
#Mapping(target = "tags",
qualifiedByName = "tagToEntity")
B toEntity(BDto b);
}
#Mapper()
public interface CMapper extends AMapper {
#Override
#Mapping(target = "tags",
qualifiedByName = "tagToDto")
CDto toDto(C b);
#Override
#Mapping(target = "tags",
qualifiedByName = "tagToEntity")
C toEntity(CDto b);
}
Pom
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<artifactId>dummy</artifactId>
<groupId>dummy</groupId>
<version>0.1.0</version>
<packaging>pom</packaging>
<properties>
<compiler-plugin.version>3.8.1</compiler-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<lombok.version>1.18.12</lombok.version>
<mapstruct.version>1.3.1.Final</mapstruct.version>
</properties>
<repositories>
<repository>
<id>projectlombok.org</id>
<url>https://projectlombok.org/edge-releases</url>
</repository>
</repositories>
<dependencies>
<!-- other stuff -->
<!-- Tools -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<!-- <scope>provided</scope> -->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</annotationProcessorPath>
<annotationProcessorPath>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
With lombok (1.18.18) and mapstruct (1.4.2.Final) everything worked after I:
added plugin lombok-mapstruct-binding
added lombok-mapstruct-binding to annotationProcessorPaths section of plugin maven-compiler-plugin
links:
github example pom.xml: https://github.com/mapstruct/mapstruct-examples/blob/master/mapstruct-lombok/pom.xml
from https://mapstruct.org/faq/ :
If you are using Lombok 1.18.16 or newer you also need to add lombok-mapstruct-binding in order to make Lombok and MapStruct work together.

Spring Boot: At least one JPA metamodel must be present

Hello everyone,
I have a problem when I run my project
Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
I don't know how to fix it.I saw some same topic and They said this problem in file pom.xml. But I don't see anything wrong in my file pom.xml. So Can somebody help me
This is my Code
File pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
DemoApplication
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
Model with #Entity
package com.example.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "contact")
public class Contact implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", nullable = false)
private int id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "email")
private String email;
#Column(name = "phone")
private String phone;
public Contact() {
super();
}
public Contact(int id, String name, String email, String phone) {
super();
this.id = id;
this.name = name;
this.email = email;
this.phone = phone;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
I don't know how to fix it
You have in your pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
But you don't have any classes with #Entity annotation defined. You need at least one of those to successfully create the Entity Manager. If not remove the above dependency.
Your SpringBootApplication relies in package com.example.demo; and your entity in package com.example.model; . Per Default #SpringBootApplication will try to look into its package and below. It cannot find your entities as they are in a different package, unless you specify it explicitly e.g. via
#EntityScan(basePackages = "com.example.model")

cassandra-driver-mapping: InvalidTypeException: Invalid 32-bits float value, expecting 4 bytes but got 6

As I got issues with spring-data-cassandra with docker as describer here I switched to use com.datastax.cassandra library for cassandra operations but I am getting issues while mapping the resulted object using entity mapper as per this link
Here is my code ...
public String getUser(String userName) {
Mapper<User> mapper = manager.mapper(User.class);
User result = mapper.get(userName); // no issue getting User
String accountNum = result.getAccountId();
return accountNum ;
}
public Account getAccount(String accountNum){
Mapper<Account> mapper = manager.mapper(Account.class);
Account account = mapper.get(accountNum); // getting error here
return account;
}
Account.java
#Table(name = "account")
public class Account {
#PartitionKey
private String accountNum;
private String accountsubtype;
private float currentbalance;
private String customertype;
private String firstname;
private boolean isactive;
private String payments;
private String status;
....
//Getters & Setters
}
pom.xml dependecies
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-mapping</artifactId>
<version>3.0.0</version>
</dependency>

CommandLine nashorn script (jjs) unable to create entity manager. Why?

CommandLine nashorn script (jjs) unable to create entity manager.
Why does this not work?
How can it be made to work (if at all)?
i.e.,
running the script looks like this...
$ jjs -cp ".;myjpaclasses-1.jar;" myNashornScript.js
i.e., where "myNashornScript.js" contains...
/* global Java, java */
print("begin test...");
var EntityManagerFactory = Java.type('javax.persistence.EntityManagerFactory');
var EntityManager = Java.type('javax.persistence.EntityManager');
var Persistence = Java.type('javax.persistence.Persistence');
var Employees = Java.type('aaa.bbb.ccc.jpa.Employees');
var employees = new Employees();
var javaImports = new JavaImporter(java.io, java.lang, java.util);
try
{
with (javaImports) {
var emf = Persistence.createEntityManagerFactory("hr_pu"); <== issue here(?)...
var em = emf.createEntityManager();
var query = em.createQuery(
"SELECT e FROM Employees e WHERE e.employeeId > ?1")
.setParameter(1, 100)
.setFirstResult(0);
var rows = query.getResultList();
//...print info on 2nd row object of returned list...
//...print returned list size...
print("rows.get(2).getFirstName()="+ rows.get(2).getFirstName()
+ "...returned row count=" + rows.size());
}
} catch (e) {
print(e.message);
}
print("end test...");
Running script from command line consistently yields the following...
begin test...
No Persistence provider for EntityManager named hr_pu
end test...
Note: fwiw, this script seems to work fine, when called from a java app...i.e.,
package aaa.bbb.ccc.jar;
import java.io.FileNotFoundException;
import java.io.FileReader;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class RunScript {
public static void main (String[] args) throws ScriptException, FileNotFoundException
{
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByName("nashorn");
engine.eval(new FileReader("src/main/resources/myNashornScript.js"));
}
}
...i.e., which yields...
begin test...
[EL Warning]: transaction: 2016-05-12 14:18:00.773--ServerSession(1829217853)--PersistenceUnitInfo hr_pu has transactionType RESOURCE_LOCAL and therefore jtaDataSource will be ignored
[EL Info]: 2016-05-12 14:18:00.78--ServerSession(1829217853)--EclipseLink, version: Eclipse Persistence Services - 2.6.3.v20160428-59c81c5
[EL Info]: connection: 2016-05-12 14:18:01.183--ServerSession(1829217853)--/file:/C:/tools/netbeansWS/myjpaclasses/target/classes/_hr_pu login successful
rows.get(2).getFirstName()=Alexander...returned row count=106
end test...
Thanks for any help/guidance!
P.S.
If it makes any difference, the persistence.xml (located in src/main/resources/META-INF of "myjpaclasses.jar") looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="hr_pu" transaction-type="RESOURCE_LOCAL">
<jta-data-source>jdbc/HR</jta-data-source>
<class>aaa.bbb.ccc.jpa.Regions</class>
<class>aaa.bbb.ccc.jpa.Employees</class>
<class>aaa.bbb.ccc.jpa.Departments</class>
<class>aaa.bbb.ccc.jpa.Locations</class>
<class>aaa.bbb.ccc.jpa.Jobs</class>
<class>aaa.bbb.ccc.jpa.Countries</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:#localhost:1521:XE"/>
<property name="javax.persistence.jdbc.user" value="HR"/>
<property name="javax.persistence.jdbc.password" value="HR"/>
</properties>
</persistence-unit>
</persistence>
The JPA "Employee" class looks like this:
package aaa.bbb.ccc.jpa;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#Entity
#Table(name = "EMPLOYEES", uniqueConstraints = #UniqueConstraint(columnNames = {"EMAIL"}))
#XmlRootElement
public class Employees implements Serializable {
#Column(name = "LAST_NAME", table = "EMPLOYEES", nullable = false, length = 25)
#Basic
private String lastName;
#Column(name = "HIRE_DATE", table = "EMPLOYEES", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
#Basic
private Date hireDate;
#ManyToOne(targetEntity = Departments.class)
#JoinColumn(name = "DEPARTMENT_ID", referencedColumnName = "DEPARTMENT_ID")
private Departments departmentId;
#Column(name = "EMPLOYEE_ID", table = "EMPLOYEES", nullable = false)
#Id
private Integer employeeId;
#ManyToOne(targetEntity = Employees.class)
#JoinColumn(name = "MANAGER_ID", referencedColumnName = "EMPLOYEE_ID")
private Employees managerId;
#Column(name = "SALARY", table = "EMPLOYEES", scale = 2, precision = 8)
#Basic
private BigDecimal salary;
#Column(name = "COMMISSION_PCT", table = "EMPLOYEES", scale = 2, precision = 2)
#Basic
private BigDecimal commissionPct;
#XmlTransient
#OneToMany(targetEntity = Employees.class, mappedBy = "managerId")
private List<Employees> employeesCollection;
#Column(name = "FIRST_NAME", table = "EMPLOYEES", length = 20)
#Basic
private String firstName;
#ManyToOne(optional = false, targetEntity = Jobs.class)
#JoinColumn(name = "JOB_ID", referencedColumnName = "JOB_ID")
private Jobs jobId;
#Column(name = "PHONE_NUMBER", table = "EMPLOYEES", length = 20)
#Basic
private String phoneNumber;
#XmlTransient
#OneToMany(targetEntity = Departments.class, mappedBy = "managerId")
private List<Departments> departmentsCollection;
#Column(name = "EMAIL", table = "EMPLOYEES", nullable = false, length = 25)
#Basic
private String email;
public Employees() {
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getHireDate() {
return this.hireDate;
}
public void setHireDate(Date hireDate) {
this.hireDate = hireDate;
}
public Departments getDepartmentId() {
return this.departmentId;
}
public void setDepartmentId(Departments departmentId) {
this.departmentId = departmentId;
}
public Integer getEmployeeId() {
return this.employeeId;
}
public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}
public Employees getManagerId() {
return this.managerId;
}
public void setManagerId(Employees managerId) {
this.managerId = managerId;
}
public BigDecimal getSalary() {
return this.salary;
}
public void setSalary(BigDecimal salary) {
this.salary = salary;
}
public BigDecimal getCommissionPct() {
return this.commissionPct;
}
public void setCommissionPct(BigDecimal commissionPct) {
this.commissionPct = commissionPct;
}
#XmlTransient
public List<Employees> getEmployeesCollection() {
return this.employeesCollection;
}
public void setEmployeesCollection(List<Employees> employeesCollection) {
this.employeesCollection = employeesCollection;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public Jobs getJobId() {
return this.jobId;
}
public void setJobId(Jobs jobId) {
this.jobId = jobId;
}
public String getPhoneNumber() {
return this.phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
#XmlTransient
public List<Departments> getDepartmentsCollection() {
return this.departmentsCollection;
}
public void setDepartmentsCollection(List<Departments> departmentsCollection) {
this.departmentsCollection = departmentsCollection;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
}
pom.xml that used to create "myjpaclasses-1.jar"...
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>aaa.bbb.ccc.jar</groupId>
<artifactId>myjpaclasses</artifactId>
<version>1</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.test.skip>false</maven.test.skip>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<timestamp>${maven.build.timestamp}</timestamp>
<maven.build.timestamp.format>yyyyMMdd.HHmmss</maven.build.timestamp.format>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc7</artifactId>
<version>12.1.0.2</version>
</dependency>
</dependencies>
<build>
<!-- use for snapshot versioning <finalName>${project.artifactId}-${project.version}-${maven.build.timestamp}</finalName> -->
<finalName>${project.artifactId}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<excludes>
<exclude>junit:junit</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<name>myjpaclasses</name>
</project>
It is likely that EntityManager / JPA library requires that persistence library class loader be the thread context class loader. With jjs -cp option, a fresh class loader is created and that is not the thread context class loader. With "java -cp", the application class loader is set as the thread context loader during initialization.
You may want to try the following in your .js script:
var EntityManagerFactory = Java.type('javax.persistence.EntityManagerFactory');
// set the thread context class to be the loader of EntityManagerFactory class
var cls = EntityManagerFactory.class;
java.lang.Thread.currentThread().contextClassLoader = cls.classLoader;
//... rest of your script..
Please let me know if this works.

Resources