Mapstruct version 1.2.0.Final not converting embedded types? - spring-boot

After working through the mapstruct tutorial I was under the impression that Mapstruct did correctly convert embedded types. I'm not sure what I am doing wrong here. any advice would be greatly appreciated...
I am working in IntelliJ 2017, with the Mapstruct plug-in installed. the failing tests included state that the embedded object, in this case, the job object inside the jobData object, is null:
java.lang.AssertionError:
Expected: is not null
but: was null
Expected :is not null
Actual :null
the following code is the source, destination, mapper interface and impl, and tests that fail. one thing that is interesting is that the impls do not have a reference to calling the converter for the embedded types.
#Entity
#Data
#NoArgsConstructor
public class JobData {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne
private Job job;
#Size(min = 5, max = 50)
private String providerName;
}
#Data
#NoArgsConstructor
public class JobDataDTO {
private static final long serialVersionUID = 1L;
private Long id;
private JobDTO jobDTO;
private String providerName;
}
#Data
#NoArgsConstructor
public class JobDTO {
private static final long serialVersionUID = 1L;
private Long id;
private JobState jobState;
private Date creationDateTime;
}
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Job {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Enumerated(value = EnumType.STRING)
private JobState jobState;
#NotNull
private Date creationDateTime;
}
#Mapper
public interface JobDataMapper {
JobDataMapper INSTANCE = Mappers.getMapper(JobDataMapper.class);
JobDataDTO jobDataToJobDataDTO(JobData jobData);
JobData jobDataDTOToJobData(JobDataDTO jobData);
JobDTO jobToJobDTO(Job job);
Job jobDTOToJob(JobDTO jobDTO);
}
#Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2018-03-24T16:33:39-0600",
comments = "version: 1.2.0.Final, compiler: javac, environment: Java 1.8.0_144 (Oracle Corporation)"
)
#Component
public class JobDataMapperImpl implements JobDataMapper {
#Override
public JobDataDTO jobDataToJobDataDTO(JobData jobData) {
if ( jobData == null ) {
return null;
}
JobDataDTO jobDataDTO = new JobDataDTO();
jobDataDTO.setId( jobData.getId() );
jobDataDTO.setProviderName( jobData.getProviderName() );
return jobDataDTO;
}
#Override
public JobData jobDataDTOToJobData(JobDataDTO jobData) {
if ( jobData == null ) {
return null;
}
JobData jobData1 = new JobData();
jobData1.setId( jobData.getId() );
jobData1.setProviderName( jobData.getProviderName() );
return jobData1;
}
#Override
public JobDTO jobToJobDTO(Job job) {
if ( job == null ) {
return null;
}
JobDTO jobDTO = new JobDTO();
jobDTO.setId( job.getId() );
jobDTO.setJobState( job.getJobState() );
jobDTO.setCreationDateTime( job.getCreationDateTime() );
return jobDTO;
}
#Override
public Job jobDTOToJob(JobDTO jobDTO) {
if ( jobDTO == null ) {
return null;
}
Job job = new Job();
job.setId( jobDTO.getId() );
job.setJobState( jobDTO.getJobState() );
job.setCreationDateTime( jobDTO.getCreationDateTime() );
return job;
}
}
public class JobDataMapperTest {
private static final long TEST_LONG = 1L;
private static final String TEST_STRING = "CUSTOMER";
private static final Date TEST_DATE = new Date();
private JobDataMapper jobDataMapper = JobDataMapper.INSTANCE;
private Job job;
private JobData jobData;
private JobDTO jobDTO;
private JobDataDTO jobDataDTO;
#Before
public void setUp() throws Exception {
job = new Job();
jobDTO = new JobDTO();
jobData = new JobData();
jobDataDTO = new JobDataDTO();
}
#Test
public void jobDataToJobDataDTO_EmbeddedJobDTODataConversion() {
jobData.setId(TEST_LONG);
job.setId(TEST_LONG);
jobData.setJob(job);
jobDataDTO = jobDataMapper.jobDataToJobDataDTO(jobData);
assertThat(jobDataDTO.getId(), is(notNullValue()));
assertThat(jobDataDTO.getId(), is(instanceOf(Long.class)));
assertThat(jobDataDTO.getId(), is(TEST_LONG));
assertThat(jobDataDTO.getJobDTO(), is(notNullValue()));
assertThat(jobDataDTO.getJobDTO(), is(instanceOf(JobDTO.class)));
assertThat(jobDataDTO.getJobDTO().getId(), is(TEST_LONG));
}
#Test
public void jobDataDTOToJobData_EmbeddedJobDTODataConversion() {
jobDataDTO.setId(TEST_LONG);
jobDTO.setId(TEST_LONG);
jobDataDTO.setJobDTO(jobDTO);
jobData = jobDataMapper.jobDataDTOToJobData(jobDataDTO);
assertThat(jobData.getId(), is(notNullValue()));
assertThat(jobData.getId(), is(instanceOf(Long.class)));
assertThat(jobData.getId(), is(TEST_LONG));
assertThat(jobData.getJob(), is(notNullValue()));
assertThat(jobData.getJob(), is(instanceOf(JobDTO.class)));
assertThat(jobData.getJob().getId(), is(TEST_LONG));
}
}

Have you checked your logs? You should have some warnings about unmapped target properties (job and jobDTO). You would need to tell MapStruct how to map them by using #Mapping.
You mapper should look like:
#Mapper
public interface JobDataMapper {
JobDataMapper INSTANCE = Mappers.getMapper(JobDataMapper.class);
JobDataDTO jobDataToJobDataDTO(JobData jobData);
JobData jobDataDTOToJobData(JobDataDTO jobData);
#InheritInverseMapping
JobDTO jobToJobDTO(Job job);
#Mapping(target = "job", source = "jobDTO")
Job jobDTOToJob(JobDTO jobDTO);
}
With #Mapping(target = "job", source = "jobDTO") you are telling MapStruct that it needs to map jobDTO into job. With #InheritInverseMapping you are saying inherit all the inverse configuration (i.e. #Mapping(target = "jobDTO", source = "job")).
You can also enable ReportingPolicy#ERROR that would fail the build in case there is an unmapped target property by using #Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)

Related

Spring boot JPARepository doesn't execute save

Im building a simple app in spring boot and having problems with saving an entity to the db. The bank account is saved normally, but the transaction is not being saved.
The weird thing is that there are no errors or anything. On the first transactionRepository call the method is exited and the app continues normally. I don't get any message in the console or and didn't find any problems using debugging.
Here is the makeTransaction() method, which executes normally until transactionRepository.save(...)
public void makeTransaction(Iban sourceIban, Iban destinationIban, double amount)
throws Exception {
BankAccountEntity sourceAccount = getBankAccountByIban(sourceIban);
BankAccountEntity destinationAccount = getBankAccountByIban(destinationIban);
Date currentDate = new Date();
TransactionEntity sourceTransaction = new TransactionEntity(sourceAccount, sourceIban, destinationIban, currentDate, amount);
TransactionEntity destinationTransaction = new TransactionEntity(destinationAccount, sourceIban, destinationIban, currentDate, amount);
if (sourceAccount.getAmountDeductible() < amount || amount < 0) { // TODO amount checker
throw new Exception(ILLEGAL_TRANSACTION_AMOUNT_TEXT); // TODO change exception type
}
sourceAccount.reduceBalance(amount);
sourceAccount.addTransaction(sourceTransaction);
destinationAccount.increaseBalance(amount);
destinationAccount.addTransaction(destinationTransaction);
bankAccountRepository.save(sourceAccount);
bankAccountRepository.save(destinationAccount);
transactionRepository.save(sourceTransaction);
transactionRepository.saveAndFlush(destinationTransaction);
}
The class has the repositories initialized as follows
private final BankAccountRepository bankAccountRepository;
private final TransactionRepository transactionRepository;
public BankAccountService(#Autowired BankAccountRepository bankAccountRepository,
#Autowired TransactionRepository transactionRepository) {
this.bankAccountRepository = bankAccountRepository;
this.transactionRepository = transactionRepository;
}
The BankAccountRepository looks like this
public interface BankAccountRepository extends JpaRepository<BankAccountEntity, UUID> {
Optional<BankAccountEntity> findByIban(String iban);
}
And the TransactionRepository like this
public interface TransactionRepository extends JpaRepository<TransactionEntity, UUID> {}
The TransactionEntity looks like this
#Entity
#Table(name = "transactions")
public class TransactionEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "transaction_id", nullable = false)
#Type(type = "uuid-char")
private Long transactionId;
private String sourceIban;
private String destinationIban;
private long dateExecuted;
private double amount;
#ManyToOne
#JoinColumn(name = "bank_account_id")
private BankAccountEntity bankAccount;
public TransactionEntity() {
}
public TransactionEntity(BankAccountEntity bankAccount, Iban sourceIban, Iban destinationIban, Date dateExecuted, double amount) {
this.bankAccount = bankAccount;
this.sourceIban = sourceIban.toString();
this.destinationIban = destinationIban.toString();
this.amount = amount;
this.dateExecuted = dateExecuted.getTime();
}
public BankAccountEntity getBankAccount() {
return bankAccount;
}
public void setBankAccount(BankAccountEntity bankAccount) {
this.bankAccount = bankAccount;
}
public Long getTransactionId() {
return transactionId;
}
public void setTransactionId(Long transactionId) {
this.transactionId = transactionId;
}
}

Many to One Relationship with #IdClass

Using Spring Data JPA & Hibernate, I am saving an object Company, that has 0 to Many AccountMapping. The AccountMappings Primary Key is a composite of a String accountNumber and the Company Primary Key. When I save a new company the COMP_NUM from the Company Object is not set into the AccountMapping object. When I use long companyNumber it is zero, and Long it is NUM. Hibernate is executing the insert statement first, but how to get it to set the primary key from company into child object ?
#Entity
#Table(name = "COMPANY")
public class Company implements Serializable {
#Id
#Column(name = "COMP_NUM")
#SequenceGenerator(name = "comp_num_seq", sequenceName = "comp_num_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "comp_num_seq")
private long number;
#OneToMany(mappedBy = "companyNumber", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<AccountMapping> accountMappings;
public Company() {
super();
}
public long getNumber() {
return this.number;
}
public void setNumber(long id) {
this.number = id;
}
public List<AccountMapping> getAccountMappings() {
return accountMappings;
}
public void setAccountMappings(List<AccountMapping> accountMappings) {
this.accountMappings = accountMappings;
}
}
#Entity
#IdClass(value = AccountMappingPK.class)
#Table(name = "ACCOUNT_MAPPING")
public class AccountMapping implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ACCNT_NUM")
private String accountNumber;
#Id
#Column(name = "COMP_NUM")
private Long companyNumber;
#Column(name = "IS_PRIMARY")
private Boolean isPrimary;
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public Long getCompanyNumber() {
return companyNumber;
}
public void setCompanyNumber(Long companyNumber) {
this.companyNumber = companyNumber;
}
public Boolean getIsPrimary() {
return isPrimary;
}
public void setIsPrimary(Boolean isPrimary) {
this.isPrimary = isPrimary;
}
}
public class AccountMapping implements Serializable {
#Column(name = "EA_ACCNT_NUM", nullable = false)
private String accountNumber;
#Column(name = "COMP_NUM", nullable = false)
private Long companyNumber;
public AccountMapping() {
// default constructor
}
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNum(String accountNumber) {
this.accountNumber = accountNumber;
}
public Long getCompanyNumber() {
return companyNumber;
}
public void setCompanyNumber(Long companyNumber) {
this.companyNumber = companyNumber;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof AccountMappingPK) {
AccountMappingPK accntPk = (AccountMappingPK) obj;
if (!(accountNumber.equals(accntPk.getAccountNumber()))) {
return false;
}
if (!(accntPk.getCompanyNumber() == (companyNumber))) {
return false;
}
return true;
}
return false;
}
#Override
public int hashCode() {
int hash = (accountNumber == null ? 1 : accountNumber.hashCode());
return (int) (hash * companyNumber);
}
}
#Entity
#IdClass(value = AccountMappingPK.class)
#Table(name = "ACCOUNT_MAPPING")
public class AccountMapping implements Serializable {
#Id
#Column(name = "ACCNT_NUM")
private String accountNumber;
#Id
#ManyToOne
#JoinColumn(name = "COMP_NUM")
private Company company;
...
}
// No annotations in this class
public class AccountMappingPK implements Serializable {
private String accountNumber;
private Company company;
...
// All the getter/setter, constructors, and so on ...
}
The Hibernate ORM documentation has more details about mapping with #IdClass: See Example 134. IdClass with #ManyToOne

Spring JPA Transaction ID

I have added an attribute to all my entities - transaction id - which is a sequence generated value that I bump up once in each transaction.
I also store the transaction id with user and start/end times so I have an audit trail for every change in the database.
What is the best way to handle storing a complete graph, where I basically only want to apply the transaction id to those entities that are actually dirty?
I can put a #PrePersist and #PreUpdate on the transaction id column, but how do I retrieve the value for the current transaction id? Is there a way to store and retrieve a value on the transaction object or other JPA controller? Do I need to use a ThreadLocal solution?
Ok, here is what I did. It seems to work in all of the use cases, though I have not done any performance testing, etc. If anyone sees anything that may be non-optimal or may fail in certain situations, please point it out.
Here is the base service class that all #Service implementations must extend:
public class BaseService
{
private final ActivityService activityService;
private final ApplicationEventPublisher applicationEventPublisher;
public static ThreadLocal<Activity> transaction = new ThreadLocal<>();
public BaseService(ActivityService activityService, ApplicationEventPublisher applicationEventPublisher)
{
this.activityService = activityService;
this.applicationEventPublisher = applicationEventPublisher;
}
Object executeWithinActivity(Updater updater)
{
boolean startedLocally = false;
try
{
if (transaction.get() == null)
{
startedLocally = true;
Activity activity = activityService.startTransaction();
transaction.set(activity);
}
return updater.execute(transaction.get());
}
finally
{
if (startedLocally)
{
applicationEventPublisher.publishEvent(new TransactionEvent());
Activity activity = transaction.get();
activityService.endTransaction(activity);
}
}
}
protected interface Updater
{
Object execute (Activity activity);
}
static class TransactionEvent
{
}
}
Activity is the entity that represents the stored transaction id:
#Entity
#Getter #Setter
#Table(name = "transactions", schema = "public", catalog = "euamdb")
public class Activity
{
#Id
#Column(name = "transaction_id", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tx_generator")
#SequenceGenerator(name = "tx_generator", sequenceName = "transaction_seq", allocationSize = 1)
private long transactionId;
#Basic
#Column(name = "user_id", length = 24)
private String userId;
#Basic
#Column(name = "transaction_start")
#CreationTimestamp
private Date transactionStart;
#Basic
#Column(name = "transaction_end")
#UpdateTimestamp
private Date transactionEnd;
#Override
public boolean equals(Object o)
{
if (this == o) return true;
if (!(o instanceof Activity)) return false;
Activity that = (Activity) o;
return transactionId == that.transactionId;
}
#Override
public int hashCode()
{
return Long.hashCode(transactionId);
}
}
ActivityService (which does not extend BaseService):
#Service
public class ActivityService
{
private final ActivityRepository activityRepository;
private final AuthUserService authService;
#Autowired
public ActivityService(ActivityRepository activityRepository, AuthUserService authService)
{
this.activityRepository = activityRepository;
this.authService = authService;
}
#Transactional
public Activity startTransaction()
{
Activity activity = new Activity();
activity.setTransactionStart(new Date());
activity.setUserId(authService.getAuthenticatedUserId());
activityRepository.save(activity);
return activity;
}
#Transactional
public void endTransaction(Activity activity)
{
activity.setTransactionEnd(new Date());
activityRepository.save(activity);
}
}
The base entity class for all entities (excepting Activity):
#MappedSuperclass
#Getter #Setter
public class BaseEntity
{
#Basic
#Column(name = "transaction_id")
private Long transactionId;
#PrePersist
#PreUpdate
public void setupTransaction ()
{
ThreadLocal<Activity> transaction = BaseService.transaction;
Activity activity = transaction.get();
long transactionId = activity.getTransactionId();
setTransactionId(transactionId);
}
}
An example of a service:
#Service
public class OrganizationService extends BaseService
{
private final OrgUserRepository orgUserRepository;
private final UserService userService;
#Autowired
public OrganizationService(ActivityService activityService,
OrgUserRepository orgUserRepository,
UserService userService,
ApplicationEventPublisher applicationEventPublisher)
{
super(activityService, applicationEventPublisher);
this.orgUserRepository = orgUserRepository;
this.userService = userService;
}
#Transactional
public OrgUser save(User user, OrgUser orgUser)
{
return (OrgUser) executeWithinActivity(activity ->
{
orgUser.setUser(userService.save(user));
return orgUserRepository.save(orgUser);
});
}
}
UserService also will extend BaseService and the save(OrgUser) method will also executeWithinActivity.
Finally, the commit listener:
#Component
public class AfterCommitListener
{
#TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
public void doAfterTxComplete(BaseService.TransactionEvent event)
{
BaseService.transaction.remove();
}
}

Unable to insert rows in database using Spring Batch + Spring data

I am working with legacy database in which db tables has no keys. For java Sake i have to used Id annotation. My goal is to read data from .dat file and insert it into table. I'm using spring batch for the above said purpose. To improve the performance threading is used. But i'm getting insertion/updation issue that i'm unable to figure out. I have referenced many sources but none seems to solve my purpose. Kindly help me out by giving some appropriate solution or reference. Thanks in advance...
Entity.java
#Entity
#Table(name = "int_repl_mkt_val")
public class IntReplMktVal implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private IntReplMktValId id;
#Column(name = "acct_sys_cd")
private String acctSysCd;
#Column(name = "co_num", nullable = false)
private Integer coNum;
#Column(name = "last_mod_tmstmp")
#Temporal(TemporalType.TIMESTAMP)
private Date lastModTmstmp;
#Column(name = "pim_owned", nullable = false)
private String pimOwned;
#Column(name = "position", nullable = false)
private BigDecimal position;
#Column(name = "pricing_plan")
private String pricingPlan;
#Column(name="source_system",nullable=false)
private String sourceSystem;
... getter and setter
}
EmbeddedClass.java
#Embeddable
public class IntReplMktValId implements Serializable
{
private static final long serialVersionUID = 4824041485763129937L;
#Column(name = "acct_id",nullable=false)
private Integer acctId;
#Column(name = "asset_id",nullable=false)
private Integer assetId;
... getter and setter
}
jpaRepository.class
#Repository
public interface IntReplMktValRepository extends JpaRepository<IntReplMktVal, IntReplMktValId>
{
}
BatchConfiguration.class
#Configuration
public class IMAPPositionBatchConfiguration
{
#Autowired
JobBuilderFactory jobBuilderFactory;
#Autowired
StepBuilderFactory stepBuilderFactory;
#StepScope
#Bean(name="imapPositionReader")
public FlatFileItemReader<IMAPPositionInputMapperDTO> reader(#Value("#{jobParameters['fileName']}") String fileName) throws IOException
{
FlatFileItemReader<IMAPPositionInputMapperDTO> newBean = new FlatFileItemReader<>();
newBean.setName("fileReader");
newBean.setResource(new InputStreamResource(FileUtils.openInputStream(new File(fileName))));
newBean.setLineMapper(this.lineMapper());
newBean.setLinesToSkip(1);
return newBean;
}
public DefaultLineMapper<IMAPPositionInputMapperDTO> lineMapper()
{
DefaultLineMapper<IMAPPositionInputMapperDTO> lineMapper = new DefaultLineMapper<>();
lineMapper.setLineTokenizer(this.lineTokenizer());
IMAPPositionReader imapPositionReader = new IMAPPositionReader();
lineMapper.setFieldSetMapper(imapPositionReader);
return lineMapper;
}
public DelimitedLineTokenizer lineTokenizer()
{
DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
tokenizer.setDelimiter("|");
tokenizer.setNames("field1","field2","field3");
tokenizer.setIncludedFields(5,4,7);
return tokenizer;
}
public ItemProcessor<IMAPPositionInputMapperDTO, IntReplMktVal> processor()
{
return new IMAPPositionProcessor();
}
#Bean(name="imapPositionBatchWriter")
public ItemWriter<IntReplMktVal> writer()
{
return new IMAPPositionWriter();
}
#Bean(name="imapPositionListener")
public JobExecutionListenerSupport jobCompletionListener()
{
return new IMAPPositionJobListener();
}
#Bean(name="imapPositionTaskExecutor")
public ThreadPoolTaskExecutor taskExecutor()
{
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(50);
executor.setMaxPoolSize(100);
return executor;
}
#Bean(name="imapPositionStep")
public Step step(#Autowired #Qualifier("imapPositionTaskExecutor")TaskExecutor taskExecutor) throws IOException
{
return stepBuilderFactory.get("imapPositionStep")
.<IMAPPositionInputMapperDTO, IntReplMktVal>chunk(100)
.reader(this.reader(null))
.processor(this.processor())
.writer(this.writer())
.taskExecutor(taskExecutor)
.build();
}
#Bean(name="imapPositionFileImportJob")
public Job importUserJob(#Autowired #Qualifier("imapPositionStep") Step step)
{
return jobBuilderFactory
.get("imapPositionFileImportJob"+new Date())
.incrementer(new RunIdIncrementer())
.listener(this.jobCompletionListener())
.flow(step)
.end()
.build();
}
}
BatchWriter.java
public class IMAPPositionWriter implements ItemWriter<IntReplMktVal>
{
#Autowired
IntReplMktValRepository intReplMktValRepository;
#Override
public void write(List<? extends IntReplMktVal> items) throws Exception
{
intReplMktValRepository.saveAll(items);
}
}
ErrorLog
2019-06-07 17:22:01,522 ERROR [scopedTarget.imapPositionTaskExecutor-4] org.hibernate.internal.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [org.hibernate.HibernateException: Duplicate identifier in table for: [com.capgroup.horizon.pricecapture.entities.IntReplMktVal#component[acctId,assetId]{assetId=274800, acctId=1}]]
NOTE: I have to insert all the data into table regardless of duplication or any other issue as keys are not defined so every data is valid.
Actually the problem was due to duplication found in persistent context which was resolved by setting the chunk size to 1.

Spring Repository - getting NullPointerException

I'm trying to save an object with Spring Data JPA. Unfortunately I always get a NullPointerException, and I don't understand why.
I followed this tutorial: https://www.callicoder.com/spring-boot-jpa-hibernate-postgresql-restful-crud-api-example/
I don't need a CRUD API so I left out the things that belong to it.
I get the following error:
java.lang.NullPointerException
at com.niclas.elitedangerousapi.handler.SystemPopulatedHandler.insertIntoDB(SystemPopulatedHandler.java:39)
at com.niclas.elitedangerousapi.Main.main(Main.java:19)
[main] ERROR c.n.e.h.SystemPopulatedHandler - null
UPDATE
I want to fill my database, but then when I want. At the end it should be so that every night a file is downloaded and then stored in the Database. Later I want to make the data accessible via an API.I want to execute this method (systemPopulatedHandler.insertIntoDB()) at the start and every x hours.
SystemPopulated.class
#Data
#Entity
#Table(name = "systems_populated")
#JsonIgnoreProperties(ignoreUnknown = true)
public class SystemPopulated {
#Id
#Column(name = "id")
private int id;
#Column(name = "edsm_id")
private long edsm_id;
#Column(name = "name")
private String name;
#Column(name = "x")
private double x;
#Column(name = "y")
private double y;
#Column(name = "z")
private double z;
#Column(name = "population")
private long population;
#Column(name = "is_populated")
private boolean is_populated;
#Column(name = "government_id")
private long government_id;
#Column(name = "government")
private String government;
#Column(name = "allegiance_id")
private int allegiance_id;
#Column(name = "allegiance")
private String allegiance;
#Column(name = "security_id")
private int security_id;
#Column(name = "security")
private String security;
#Column(name = "primary_economy_id")
private int primary_economy_id;
#Column(name = "primary_economy")
private String primary_economy;
#Column(name = "power")
private String power;
#Column(name = "power_state")
private String power_state;
#Column(name = "power_state_id")
private int power_state_id;
#Column(name = "needs_permit")
private boolean needs_permit;
#Column(name = "updated_at")
private long updated_at;
#Column(name = "controlling_minor_faction_id")
private int controlling_minor_faction_id;
#Column(name = "controlling_minor_faction")
private String controlling_minor_faction;
#Column(name = "reserve_type_id")
private int reserve_type_id;
#Column(name = "reserve_type")
private String reserve_type;
}
My SystemPopulatedRepository.class
#Repository
public interface SystemPopulatedRepository extends JpaRepository<SystemPopulated, Integer> {
}
My Class where i want to InsertIntoDB SystemPopulatedHandler.class
#Slf4j
public class SystemPopulatedHandler {
#Autowired
private SystemPopulatedRepository systemPopulatedRepository;
public void insertIntoDB() {
BufferedReader reader;
try {
reader = new BufferedReader( new FileReader(DOWNLOAD_SAVE_PATH + FILE_NAME_SYSTEMS_POPULATED) );
String line = reader.readLine();
while( line != null ){
ObjectMapper mapper = new ObjectMapper();
systemPopulatedRepository.save( mapper.readValue( line, SystemPopulated.class ) );
line = reader.readLine();
}
reader.close();
}
catch( Exception e ) {
e.printStackTrace();
log.error( e.getLocalizedMessage() );
}
}
}
My Main.class
#SpringBootApplication
#EnableJpaRepositories
public class Main {
public static void main( String[] args ) {
SpringApplication.run( Main.class, args );
SystemPopulatedHandler systemPopulatedHandler = new SystemPopulatedHandler();
systemPopulatedHandler.insertIntoDB();
}
}
The problem is that you create SystemPopulatedHandler yourself with
SystemPopulatedHandler systemPopulatedHandler = new SystemPopulatedHandler();
That way spring isn't injecting the repository into your class because that works only if spring creates the class.
But if you want to populate a database at startup (at least it seems that you try to do that) you should check out flyway (or 85.5 in this documentation: https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html)
If you can't use flyway for some reason you can add the following code to the main class:
#Bean
public SystemPopulatedHandler systemPopulatedHandler(SystemPopulatedRepository repository) {
SystemPopulatedHandler systemPopulatedHandler = new SystemPopulatedHandler(repository);
systemPopulatedHandler.insertIntoDB()
return systemPopulatedHandler;
}
Afterwards add the constructor to the SystemPopulatedHandler class:
public SystemPopulatedHandler(SystemPopulatedRepository systemPopulatedRepository) {
this.systemPopulatedRepository = systemPopulatedRepository;
}
and remove the #Autowired annotation.
update
You also need to add the missing annotations as mentioned by this answer: https://stackoverflow.com/a/55767393/2248239
update 2
If you want to do that action periodically you can use scheduling (like in this guide https://spring.io/guides/gs/scheduling-tasks/)
Actually that's pretty easy:
Don't do the changes I mentioned above except adding the missing annotations and just do the following:
Add #Component to SystemPopulatedHandler
Add #Scheduled to insertIntoDB() in SystemPopulatedHandler
And add #EnableScheduling to the main class
For #Scheduled just read the guide it describes what you can do with the annotation.
This is due to the repository you have auto wired is not wiring the bean.
Please annonate your repository with #Repository
And in main class specify #EnableJpaRepository.
See the spring data JPA docs for more details
The problem is that you are instantiating the SystemPopulatedHandler bean without using ApplicationContext or BeanFactory, So it's not maintained by IOC container.
To use DI in a SpringBoot application all you need is to just auto wire SystemPopulatedHandler in your controller or service and then you can call insertIntoDB() method.
Since you are using spring boot and example is so simple you needn't make separate Configuration for beans.
#Controller
public class SystemPopulatedController {
#Autowired
private SystemPopulatedHandler systemPopulatedHandler;
#RequestMapping("/")
public void insertIntoDB() {
systemPopulatedHandler.insertIntoDB();
}
}
Many thanks to all of you. I have solved the problem as follows:
SystemPopulatedRepository.class
#Repository
public interface SystemPopulatedRepository extends JpaRepository<SystemPopulated, Integer> {
}
´´´
SystemPopulatedHandler.class
#Slf4j
#Component
public class SystemPopulatedHandler {
#Autowired
private SystemPopulatedRepository systemPopulatedRepository;
#PostConstruct
#Scheduled(cron = "0 0 0 * * *")
public void insertIntoDB() {
BufferedReader reader;
try {
reader = new BufferedReader( new FileReader(DOWNLOAD_SAVE_PATH + FILE_NAME_SYSTEMS_POPULATED) );
String line = reader.readLine();
while( line != null ){
ObjectMapper mapper = new ObjectMapper();
systemPopulatedRepository.save( mapper.readValue( line, SystemPopulated.class ) );
line = reader.readLine();
}
reader.close();
}
catch( Exception e ) {
e.printStackTrace();
log.error( e.getLocalizedMessage() );
}
}
}
´´´
Main.class
#SpringBootApplication
#EnableJpaRepositories
#EnableScheduling
public class Main {
public static void main( String[] args ) {
SpringApplication.run( Main.class, args );
FileHandler fileHandler = new FileHandler();
}
}

Resources