How do i create new table in database from same entity JPA Spring boot? - spring

I want to create a new table every day with the full date as table name if the day is new create table for the that day
I have seen that when I change #Table(name="") name to a new string it make a new table but I can't automate this work
#Entity
#Table(name="orders_25_10_2021")
public class game2data {
#Id
private int cid;
private String name;
private Integer address;
private Integer gender;
private String date;
private String Done;
In simple words, I want to pass the name of #Table(name="") as a variable with dynamic according to date.

You can achieve this custom naming strategy by extending SpringPhysicalNamingStrategy and overriding its toPhysicalTableName method :
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;
import org.springframework.context.annotation.Configuration;
#Configuration
public class TableNamingStrategy extends SpringPhysicalNamingStrategy {
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd_MM_YYYY");
public static final PhysicalNamingStrategyStandardImpl INSTANCE =
new PhysicalNamingStrategyStandardImpl();
#Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
if (!name.getText().equals("game2data"))
return name;
StringBuilder customName =
new StringBuilder("orders").append('_').append(DATE_FORMATTER.format(LocalDate.now()));
return new Identifier(customName.toString(), name.isQuoted());
}
}
and registering it in your application.properties:
spring.jpa.hibernate.naming.physical-strategy=com.stackoverflow.questions.TableNamingStrategy
and removing the #Table annotation from the game2data entity.
This method is limited by the fact table names are determined at application start-up.
As a proof of concept, here's a way to update the table name every day by extending StatementInspector. By using this, you won't be able to read old data. You'll also have to use custom implementations of the JpaRepository's methods to create the new table every day before you insert data in it.

Related

Spring can't find implementation

Here is my folder structure:
In my IAppUserMapper I have a method to convert every AppUser entity instance to Data Transfer Object Model. Here is the code in IAppUserMapper interface:
import com.server.ecommerceapp.dto.AppUserDTO;
import com.server.ecommerceapp.model.AppUser;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
#Mapper
public interface IAppUserMapper {
IAppUserMapper appUserMapper = Mappers.getMapper(IAppUserMapper.class);
#Mapping(target = "username")
#Mapping(target = "email")
#Mapping(target = "password")
#Mapping(target = "roles", expression = "java(appUser.getRoles().stream().map(this::getRoleName).collect(Collectors.toList()))")
AppUserDTO toAppUserDTO(AppUser appUser);
default String getRoleName(Role role) {
return role.getRoleName();
}
}
And here is the MapperConfiguration class code where I configure both Product and user mappers:
import com.server.ecommerceapp.mapper.IAppUserMapper;
import com.server.ecommerceapp.mapper.IProductMapper;
import org.mapstruct.factory.Mappers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MapperConfiguration {
#Bean
public IAppUserMapper appUserMapper() {
return Mappers.getMapper(IAppUserMapper.class);
}
#Bean
public IProductMapper productMapper() {
return Mappers.getMapper(IProductMapper.class);
}
}
The error I get:
Error creating bean with name 'appUserMapper' defined in class path
resource
[com/server/ecommerceapp/configuration/MapperConfiguration.class]:
Bean instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [com.server.ecommerceapp.mapper.IAppUserMapper]: Factory
method 'appUserMapper' threw exception; nested exception is
java.lang.RuntimeException: java.lang.ClassNotFoundException: Cannot
find implementation for com.server.ecommerceapp.mapper.IAppUserMapper
I was told I should make META-INF package in resources, with service package and the com.server.ecommerceapp.mapper.AppUserMapper txt with the content same as the name of the file, so that Spring can scan and find the package following the path:
src/main/resources/META-INF/service/com.server.ecommerceapp.mapper.AppUserMapper
but it didnt work. Any ideas how to solve this, and by the way, is it bad practise to start interface names with capital I cause Im coming from ASP?
Edit:
I added #Mapper(componentModel = "spring") to my interfaces and implemented them as DI with Autowired. I dont know if its related to that problem that I had but now I get error that it cant find collectors. Im trying to map a collection of Roles from AppUser to AppUserDTO. Here are both AppUser and AppUserDTO classes:
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Data
public class AppUser {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(name = "username", nullable = false, unique = true)
private String username;
#Column(name = "email", nullable = false, unique = true)
private String email;
#Column(name = "password", nullable = false)
private String password;
#ManyToMany(fetch = EAGER)
private Collection<Role> roles;
}
And DTO:
#NoArgsConstructor
#AllArgsConstructor
#Data
public class AppUserDTO {
private String username;
private String email;
private String password;
private Collection<String> roles;
}
So you're using Spring, but you are trying to not use Spring.
You should make your mappers use Spring component model:
#Mapper(componentModel = "spring")
public interface MyMapper {
Target map(Source source);
}
Check docs for dependency injection: https://mapstruct.org/documentation/stable/reference/html/#using-dependency-injection
Or do it with shared configuration: https://mapstruct.org/documentation/stable/reference/html/#shared-configurations
After that you can just #Autowired MyMapper myMapper; as any other Spring bean. No need to create instance in interface (the "Mappers.getMapper" thing) and no need to create mappers in java configuration, bean creation will be handled by framework.
#Mapping(target = "roles", expression = "java(appUser.getRoles().stream().map(this::getRoleName).collect(Collectors.toList()))")
now I get error that it cant find collectors
You are using an expression with Collectors class. As stated in the documentation https://mapstruct.org/documentation/stable/reference/html/#expressions:
Please note that the fully qualified package name is specified because MapStruct does not take care of the import of the TimeAndFormat class (unless it’s used otherwise explicitly in the SourceTargetMapper). This can be resolved by defining imports on the #Mapper annotation.
So you either need to fully qualify java.util.stream.Collectors in your expression or set "imports" parameter in #Mapper annotation: #Mapper(imports = Collectors.class).
I would also say, you could just write a normal Java method for roles mapping and not be dealing with expressions. But that's up to your taste.
The file name of the service should be the interface and its content the implementation. You have named it by the implementation.

How to make Set using spring-data-aerospike

Environment:
spring-boot v2.0.4 RELEASE
spring-data-aerospike v2.0.1.RELEASE
java - 8
Here are my application code and properties.
// application.properties
aerospike.hosts=xxx:3000
aerospike.namespace=test
// aerospike configuration class
#Configuration
#RequiredArgsConstructor
#EnableConfigurationProperties(AerospikeConfiguration.AerospikeConfigurationProperties.class)
#EnableAerospikeRepositories(basePackageClassses = TestAeroRepository.class)
public class AerospikeConfiguration extends AbstractAerospikeDataConfiguration {
private final AerospikeConfigurationProperties aerospikeConfigurationProperties;
#Override
protected Collection<Host> getHosts() {
return Host.parseServiceHosts(aerospikeConfigurationProperties.getHosts());
}
#Override
protected String nameSpace() {
return aerospikeConfigurationProperties.getNamespace();
}
#Data
#Validate
#ConfigurationProperties("aerospike")
public static class AerospikeConfigurationProperties {
#NotEmpty
String hsots;
#NotEmpty
String namespace;
}
}
# Entity class
#Value
#Document
#Builder(toBuilder = true)
#AllArgsConstructor
public class testEntity() {
#Id
int id;
#Field
String name;
#Field
String timestamp;
}
#Repository
public interface TestAeroRepository extends AerospikeRepository<TestEntity, Integer> {
}
public interface TestAeroService {
void save();
}
#Service
#RequiredArgsConstructor
public class TestAeroServiceImpl implements TestAeroService {
private final TestAeroRepository testAeroRepository;
#Override
public void save(TestEntity entity) {
testAeroRepository.save(entity);
}
}
I checked Aerospike client connection has no problem.
But error is occurred when save() method is executed.
org.springframework.cglib.core.ReflectUtils.defineClass(Ljava/lang/String;[BLjava/lang/ClassLoader;Ljava/security/ProtectionDomain;Ljava/lang/Class;)Ljava/lang/Class;
Have to make sets before execute the application? I didn't make sets.
Any problem with my code?
You’re using an old version of spring-data-aerospike (2.0.1.RELEASE was released on April 2019) is there any chance you can upgrade to the latest version? 2.4.2.RELEASE
You can see how to setup a simple spring data aerospike application here: https://medium.com/aerospike-developer-blog/simple-web-application-using-java-spring-boot-aerospike-database-and-docker-ad13795e0089
Please share the entire project’s code and the entire exception.
I would look into:
The configuration class (The Aerospike Beans creation).
The content of the testEntity class - are you using #Id annotation on the primary key field?
Extending the repository class with specifying the testEntity object (… extends AerospikeRepository<testEntity, Object> {) you can see an example in the link I added.
The set is automatically created and takes the name of your object class, which is testEntity in your case. For example, based on your code, if you do not specify a collection in the #Document annotation a set named "testEntity" will automatically be created. I added the #Document(collection = "testEntitys") annotation and all I did was create two set. Once you insert your first record, run the "SHOW SETS" aql command and it will be there. So that's one way to do it.

Add Two Conditions in JpaRepository

i am trying to do a POC - using JpaRepository filter out the data by adding two conditions.
I have written a code like below
public interface TemplateRepository extends JpaRepository<Template, Long> {
List<Template> findByTemplateNameContains(String templateName);//This is Working Fine
List<Template> findByTemplateNameAndActiveFlagContains(String templateName, String activeFlag);// My POC
}
templateName column is a VARCHAR2 and activeFlag is a Char in the Oracle Database. I am trying to filter the data with both templatename
and activeFlag.
I pass the input object in SoapUI app (POST) request.
{
"netting":"karu_test",
"activeFlag": "Y"
}
but I get the below error
"Parameter value [%Y%] did not match expected type [java.lang.Character (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [%Y%] did not match expected type [java.lang.Character (n/a)]"
I understand this error like, the ACTIVE_FLAG column is CHAR(1) so type mismatch happend. But how to achieve the same functionality ?
More over .. how to use multiple table joins and condition in JpaRepository
I changed the type of activeFlag to Char still i get the same error.
Template class
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.Date;
import java.util.List;
#Entity
#Table(name="TEMPLATE_DEF")
#Getter
#Setter
public class Template {
#Column(name="TEMPLATE_ID")
#Id
private String nettingTemplateId;
#Column(name="TEMPLATE_NAME")
private String templateName;
#Column(name="LAST_UPDATE")
private Date lastUpdate;
#Column(name="UPDATE_USER_ID")
private Integer updUsrId;
#Column(name="ACTIVE_FLAG")
private char activeFlag;
#Column(name="VERSION")
private Integer Version;
#Column(name="CREATION_DATE")
private Date creationDate;
#Column(name="CREATE_USER_ID")
private Integer createUsrId;
}
Please try the below JPA Query
List<Template> findByTemplateNameContainingAndActiveFlagContaining(String templateName, Character activeFlag);
Your Active flag is a char so no point in putting containing for activeFlag rather do a exact match, change method signature to
List<Template> findByTemplateNameContainsAndActiveFlag(String templateName, char activeFlag);// My POC
I have tested it it will match name with like and activeFlag based on value of it

how to persist only the properties that were assigned in spring crudrepository?

I'm running in to a small problem with persisting an object. Here's an example of what my entity class looks like.
#Entity
public class Example(){
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private int number;
private String sentence;
/* No arg const, getters and setters omitted */
CrudRepository Interface:
#Repository
public interface ExampleRepository extends CrudRepository<Example, Integer>
{}
Service that implements the Interface:
#Service
public class ExampleService{
#Autowired
public ExampleRepository exampleRepository;
public void save(Example example){
exampleRespository.save(example)
}
}
Inside of CommandLineRunner:
Example example1 = new Example();
example1.sentence("Hello World!");
exampleService.save(example1);
Now the problem that I'm running into is that even though I didn't assign any value to the property number, it is still getting persisted as 0. How do I stop that property from getting assigned a value of 0 and make it null?
Change
private int number;
into
private Integer number;
the above solution is fine but again if you see in insert query for your save function, It insert all 3 column even you assigned only once column example1.sentence("Hello World!");
you can used #DynamicInsert(true) #DynamicUpdate(true) on Entity level,
this will fire query as
insert into example(sentence) values('Hello World!');
so query performance will increase

Spring Boot repository save does not work (only shows a select)

I'm facing for hours with a strange proceeding in Spring Boot when try to save a mapped entity.
The entity class with a composite key that must all be set by the user is as follows:
package model
import javax.persistence.*
#Entity
#Table(name = 'MY_TABLE')
#IdClass(MyIdClass.class)
class MyClass implements Serializable{
#Id
#Column(name = "MY_COLUMN_1")
Long column1
#Id
#Column(name = "MY_COLUMN_2")
Long column2
#Id
#Column(name = "MY_COLUMN_3")
String column3
#Id
#Column(name = "MY_COLUMN_4")
Date date1
#Column(name = "MY_COLUMN_5")
Date date2
#Column(name = "MY_COLUMN_6")
BigDecimal column6
}
#Embeddable
class MyIdClass implements Serializable{
Long column1
Long column2
String column3
Date date1;
}
The corresponding repository is:
package repository
import org.springframework.data.repository.CrudRepository
interface MyRepository extends CrudRepository<MyClass, Long>{
}
My service is:
package service
import model.MyClass
import repository.MyRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
#Service
class MyService {
#Autowired
MyRepository repository
void save(MyClass myClass) {
repository.save(myClass)
}
}
My controller mounts a MyClass object with all data set, including the composite key. When it calls the service save method the object is not inserted in the database. I saw the logs and checked that there is a SELECT in MY_TABLE instead of INSERT. I tried not to inform the composite key in the object and then the save method did an INSERT with error due to null values in the primary key.
I really don't understand why the insertion is not done when the composite key has values. How can I solve it?
I've already tried with #Transactional in service class and didn't work. I didn't do any Transaction configuration in the project since Spring Boot delivers it as default.
Thanks.
It seems you are using MyIdClass as the Id for MyClass. So, the Repository should be:
interface MyRepository extends CrudRepository<MyClass, MyIdClass>{
}
Hope this help.
I take your code sample and tried it on a sample Spring Boot project, where I was able to save to H2 DB (In memory) with #Embeddable & #EmbeddedId annotations. If you would like to verify, you can clone the GitHub repo and run the BootJpaApplication.java as a Java Application.
After execution access the H2 console with the below link from local where table details can be verified.
http://localhost:8080/h2-console
https://github.com/sujittripathy/springboot-sample.git
Hope the detail helps:)

Resources