Greendao not generating import of ToMany joiner dao. How can I do this?
I'm creating Book and BookStore, trying save list of books in book store by Custom joiner. After build trying generated Joiner JoinBookStoreWithBookDao not importing in BookStoreDao but exists.
Sources
Book.java
package com.example.valery.stackoverflowsample.dao;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Generated;
#Entity
public class Book {
#Id
private long id;
public Book() {
}
}
BookStore.java
package com.example.valery.stackoverflowsample.dao;
import com.example.valery.stackoverflowsample.dao.joiner.DaoSession;
import com.example.valery.stackoverflowsample.dao.joiner.JoinBookStoreWithBook;
import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.JoinEntity;
import org.greenrobot.greendao.annotation.ToMany;
import java.util.ArrayList;
import java.util.List;
#Entity
public class BookStore {
#Id
private long id;
#ToMany
#JoinEntity(
entity = JoinBookStoreWithBook.class,
sourceProperty = "bookStoreId",
targetProperty = "bookId"
)
private List<Book> mBooks;
}
JoinBookStoreWithBook.java
package com.example.valery.stackoverflowsample.dao.joiner;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Generated;
#Entity
public class JoinBookStoreWithBook {
#Id
private long id;
private long bookId;
private long bookStoreId;
}
I found reason. Joiner should be inside of package for "parent", he can't be in another package.
Related
In codebase where model classes use Model from https://github.com/baomidou/mybatis-plus
public class SomeDomainClass extends Model<SomeDomainClass> {
How to mock (simulate) MyBatis-Plus data access layer for unit testing?
Longer code example
package com.company.project.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import lombok.experimental.Tolerate;
import java.io.Serializable;
import java.math.BigDecimal;
#Data
#EqualsAndHashCode(callSuper = false)
#Accessors(chain = true)
#TableName("t_order")
#Builder
public class Order extends Model<Order> {
private static final long serialVersionUID = 1L;
#ApiModelProperty(name = "id" , value = "id")
#TableId(value = "f_id", type = IdType.AUTO)
private Long id;
#ApiModelProperty(name = "orderId" , value = "orderId")
private Long orderId;
I need to convert entity's field on fetch and according to this official example I've tried to do that with custom setter:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
#Table("entity")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder(builderClassName = "EntityBuilder")
public class Entity {
#Id
private String someId;
#Transient
private String entityName;
#Column("entity_name")
public String getEntityNameUnmodified() {
return this.entityName;
}
#Column("entity_name")
public void setEntityNameUnmodified(String em) {
this.entityName = em + " Some modification";
}
}
But this completely doesn't work and as a result I get Entity with entityName == null.
I've downloaded GitHub example and run it locally and everything worked. What's wrong with my code?
My code didn't work because of missing #AccessType(AccessType.Type.PROPERTY) annotation.
Working solution:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.AccessType;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
#Table("entity")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder(builderClassName = "EntityBuilder")
#AccessType(AccessType.Type.PROPERTY) // IMPORTANT !!!
public class Entity {
#Id
private String someId;
#Transient
private String entityName;
#Column("entity_name")
public String getEntityNameUnmodified() {
return this.entityName;
}
#Column("entity_name")
public void setEntityNameUnmodified(String em) {
this.entityName = em + " Some modification";
}
}
The thing is that Spring Data JDBC uses fields as accessors for entity's columns by default and without that annotation setters and getters are ignored. Enabling PROPERTY access type solves the problem.
i'm not a professional in spring boot / hibernate jpa, so i apologize in advance.
I have 2 Entities, one called Document and the other called Bgo. Document is the parent table and Bgo the child. So my objective is to create the Document and after that, Bgo will receive it's id, for example:
When i create the first Document, it will have id_document = 1, so after that, the Bgo will have id_document = 1 as well.
I want to populate the child's id with the parent's id.
Here is the Parent Entity:
package com.testing.testing.models;
import java.io.Serializable;
import java.sql.Date;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
#Entity
#Table(name = "DOCUMENT")
public class Document implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id_document;
private int num_document;
private Date date;
#OneToOne(mappedBy = "document", cascade = CascadeType.ALL)
private Bgo bgo;
public Document() {
}
public Document(Bgo bgo) {
this.id_document = bgo.getId_document();
this.bgo = bgo;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Bgo getBgo() {
return bgo;
}
public void setBgo(Bgo bgo) {
this.bgo = bgo;
}
public long getId_document() {
return id_document;
}
public void setId_document(long id_document) {
this.id_document = id_document;
}
public int getNum_document() {
return num_document;
}
public void setNum_document(int num_document) {
this.num_document = num_document;
}
}
And Child Entity:
package com.testing.testing.models;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
#Entity
#Table(name = "BGO")
public class Bgo implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long id_document;
private String name_bgo;
private int num_bgo;
#OneToOne
#PrimaryKeyJoinColumn(name = "id_document", referencedColumnName = "id_document")
private Document document;
public Bgo() {
}
public Bgo(Document document) {
this.id_document = document.getId_document();
this.document = document;
}
public long getId_document() {
return id_document;
}
public void setId_document(long id_document) {
this.id_document = id_document;
}
public String getName_bgo() {
return name_bgo;
}
public void setName_bgo(String name_bgo) {
this.name_bgo = name_bgo;
}
public int getNum_bgo() {
return num_bgo;
}
public void setNum_bgo(int num_bgo) {
this.num_bgo = num_bgo;
}
public Document getDocument() {
return document;
}
public void setDocument(Document document) {
this.document = document;
}
}
I used the term PrimaryKeyJoinColumn cause i want Bgo's id to be the Document's id, thats why i also used the same name "id_document". So Bgo's id will be Primary and Foreign at the same time (if it is wrong please tell me a better way to do it, knowledge is always welcoming)
I have also Document's Repository:
package com.testing.testing.repository;
import com.testing.testing.models.Document;
import org.springframework.data.jpa.repository.JpaRepository;
public interface DocumentRepository extends JpaRepository<Document, Long> {
}
And Bgo's:
package com.testing.testing.repository;
import com.testing.testing.models.Bgo;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BgoRepository extends JpaRepository<Bgo, Long> {
}
Document's Controller:
package com.testing.testing.controllers;
import java.util.List;
import com.testing.testing.models.Document;
import com.testing.testing.repository.DocumentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
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(value="/document")
public class DocumentController {
#Autowired
DocumentRepository documentRepository;
#GetMapping
public List<Document> listDocument() {
return documentRepository.findAll();
}
#PostMapping
public Document createDocument(#RequestBody Document document) {
return documentRepository.save(document);
}
}
Bgo's Controller:
package com.testing.testing.controllers;
import java.util.List;
import com.testing.testing.models.Bgo;
import com.testing.testing.repository.BgoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping(value="/bgo")
public class BgoController {
#Autowired
BgoRepository bgoRepository;
#GetMapping
public List<Bgo> listBgo() {
return bgoRepository.findAll();
}
}
So basically, the problem is this:
enter image description here
When i create a new Document and also the Bgo object, Bgo's id is 0, as you can see in the image, the object Bgo has the "id_document = 0 ", shouldn't it be id_document = 1 ?
And when i try to list Bgo, it still shows id_document = 0:
enter image description here
That's how i want the database to be:
enter image description here
As you can see, they are different tables. But Document has the primary key and is the parent, and Bgo is a child cause it is receiving Document's id. As you can see, Bgo's has the Document's id, it is primary and foreign at the same time, thats why i used PrimaryKeyJoinColumn. Both of them have the same id, Document's id equals Bgo's id. So whenever i create a Document and a Bgo at the same time, both of them should have the same id.
you might need to use the inheritance functionality comes with Spring instead. have a look at this:
https://www.baeldung.com/hibernate-inheritance
you can for instance use the #Inheritance(strategy = InheritanceType.SINGLE_TABLE)
on your parent table. and then you will annotate the child with just the #Entity
and then you will extend it as you normally do in a normal Inheritance scenario
so your parent class will look like so:
import java.sql.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.Table;
import javax.persistence.InheritanceType;
#Entity
#Table(name = "Document")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Document {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private Integer Id;
#Column(name = "num_document")
private int num_document;
#Column(name = "date")
private Date date;
public Document() {
}
public Document( int num_document, Date date) {
super();
this.num_document = num_document;
this.date = date;
}
}
and the child will look like so
import java.sql.Date;
import javax.persistence.Entity;
import javax.persistence.Table;
#Entity
#Table(name = "Bgo")
public class Bgo extends Document {
#Column(name = "name_bgo")
private String name_bgo;
#Column(name = "num_bgo")
private int num_bgo;
public Bgo(String name_bgo, int num_bgo) {
super();
this.name_bgo = name_bgo;
this.num_bgo = num_bgo;
}
public Bgo() {
super();
}
}
and you will have One JPARepository which looks like so;
import org.springframework.data.jpa.repository.JpaRepository;
public interface DocumentRepository extends JpaRepository<Document, Integer> {
}
the result of this code, will generate one table in your database with one extra column dtype which will identify the record to be either Bgo or any other type you might need to add in the future. this is the idea behind inheritance anyway
I hope this helped
Here are the three different inheritance strategy you have using spring
Single mapping is the default mapping
And it uses #inheritance(strategy = InheritanceType.SINGLE_TABLE) annotation to the parent class
In this strategy, the parent class is a table and all its children will be specified in a discriminator column in the parent table. The column called dtype and it contain the name of the entity as a value.
Table per the class strategy is similar to the superclass strategy but the superclass is also an entity you need to avoid this one if you want to make so many join queries
Joined table strategy is used the same as above. In this strategy, the subclasses and the superclass will all be tables in the database, but the subclasses will not inherit the filed of the superclass, it is useful if we want to apply data integrity and null constrains on some field
for more details look here
https://thorben-janssen.com/complete-guide-inheritance-strategies-jpa-hibernate/
choose which one will suit your case and then you can use the implementation I provided in the first answer and you only need to change the this annotation
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
I have created a pretty small and simple Spring Boot app using the Oracle database and some JPA queries.
This is the code snippet which is not returning data, which is actually exists in database.
letterRecipientNonOas = letterRecipientNonOasRepository
.findById(Long.valueOf(letterRecipientDTO.getNonOas().getId()))
.orElseThrow(() -> new EntityNotFoundException(LetterRecipientNonOas.class,
Constant.MESSAGE_ENTITY_NOT_FOUND));
here findById is returning empty result set.
this is my repository
package com.care.document.repository;
import java.util.List;
import java.util.Optional;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.care.document.model.LetterRecipientNonOas;
/**
* The Interface LetterRecipientNonOasRepository.
*/
#Repository
public interface LetterRecipientNonOasRepository extends PagingAndSortingRepository<LetterRecipientNonOas, Long> {
Optional<LetterRecipientNonOas> findByLetterId(Long id);
Optional<LetterRecipientNonOas> findByTitleIgnoreCase(String title);
List<LetterRecipientNonOas> findByTitleContainingIgnoreCase(String title);
List<LetterRecipientNonOas> findAllByTitleIgnoreCaseAndIdNot(String title, Long recipientId);
List<LetterRecipientNonOas> findAllByIdAndLetterId(long id, long letterId);
}
and this is my model class:
package com.care.document.model;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import org.springframework.lang.Nullable;
import com.care.admin.model.BaseEntity;
import com.care.admin.util.CommonUtil;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#FieldDefaults(level = AccessLevel.PRIVATE)
#Entity
#Table(name = "letter_recipient_non_oas")
public class LetterRecipientNonOas extends BaseEntity implements Serializable {
#Id
Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "letter_id")
Letter letter;
Integer recipientType; // Action/Info
//byte recipientSubType; // Internal/External/NonOAS
byte recipientCategory; //Internal/External
int orderNo;
String title;
#Nullable
String remarks;
String address;
#PrePersist
private void prePersist() {
this.id = CommonUtil.generateID(this.atRegion);
}
}
I tested, tried different ways but of no use.
There are a couple of scenarios how one might get this impression:
You are looking at the wrong database.
The data isn't there yet when you try to load it, but is when you check.
JPAs caches are known to create such scenarios rather efficiently.
The data looks a little different than you think. This could be caused by invisible or easy to miss content like spaces or even control characters.
You check the database within the transaction that created the data or with a session that allows dirty reads and the insert that created the data wasn't committed yet.
I'm continuing the development of a system that uses the React JavaScript library (and related stuff) on the front end and Spring Data REST, Hibernate, PostgreSQL and related stuff on the back end.
This system will be used by people who may own one or more companies and their clients. This means that most/all model objects will have a reference to the Company(ies) that they belong to. Also, company owners will have a few Employees that will have higher level access on this system (or these will be the owners themselves).
I need to implement a functionality where, when a company is inserted in the database, an employee is inserted as well. Also, if one fails, both must fail. Because of how the model was set up, I'm sending an Employee object to be saved, and, within it, the new Company, like this (using Axios):
employee: {
// ...,
company: {
// ....
}
}
Problem is, when the save method is called in the back end, the Company member of the Employee object is null. I've tried a few things, like messing with the relationship, adding an Employee list to the Company object, passing the Company object separately, but nothing worked.
What else could I try? Here are some classes:
Record.java
package xxx.model.common;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.validation.constraints.NotNull;
import lombok.Data;
#Data
#MappedSuperclass
public abstract class Record {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
#NotNull
#Column(name = "deleted")
protected Boolean isDeleted = false;
#NotNull
#Column(name = "enabled")
protected Boolean isEnabled = true;
}
Company.java
package xxx.model;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import xxx.common.Record;
// ...
import lombok.Data;
import lombok.EqualsAndHashCode;
#Data
#EqualsAndHashCode(callSuper=false)
#Entity
#Table(name="company")
#AttributeOverrides( { #AttributeOverride(name = "id", column = #Column(name = "id_company")) } )
public class Company extends Record {
/*
* ...
*/
// Necessary for Hibernate
protected Company() {}
public Company(/* ... */) {
/*
* ...
*/
}
}
Registry.java
package xxx.model.common;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
#Data
#EqualsAndHashCode(callSuper=false)
#MappedSuperclass
public abstract class Registry extends Record {
#NotBlank
#Column(name = "code", length = 15)
protected String code;
#NotBlank
#Column(name = "name", length = 40)
protected String name;
}
RegistrySingleCompany.java
package xxx.model.common;
import javax.persistence.CascadeType;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import com.fasterxml.jackson.annotation.JsonBackReference;
import xxx.model.Company;
import lombok.Data;
import lombok.EqualsAndHashCode;
#Data
#EqualsAndHashCode(callSuper=false)
#MappedSuperclass
public class RegistrySingleCompany extends Registry {
#ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.MERGE }, optional= false)
#JoinColumn(name="id_company")
protected Company company;
}
Employee.java
package xxx.model;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import xxx.model.common.RegistrySingleCompany;
import lombok.Data;
import lombok.EqualsAndHashCode;
#Data
#EqualsAndHashCode(callSuper=false)
#Entity
#Table(name="employee")
#AttributeOverrides( { #AttributeOverride(name = "id", column = #Column(name = "id_employee")) } )
public class Employee extends RegistrySingleCompany {
/*
* ...
*/
// Necessary for Hibernate
protected Employee() {}
}
EmployeeRepositoryCustom.java
package xxx.repository.custom;
import org.springframework.data.repository.query.Param;
import xxx.model.Employee;
public interface EmployeeRepositoryCustom {
<S extends Employee> S save(S entity);
}
EmployeeRepositoryCustomImpl.java
package xxx.repository.custom;
import javax.persistence.PersistenceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestBody;
import xxx.model.Employee;
public class EmployeeRepositoryCustomImpl implements EmployeeRepositoryCustom {
#Override
#Transactional
public <S extends Employee> S save(#RequestBody S entity) {
/*
* ...
*/
return entity;
}
}
EmployeeProjection.java
package xxx.model.projection;
import org.springframework.data.rest.core.config.Projection;
import xxx.model.Employee;
#Projection(name = "employeeProjection", types = { Employee.class })
public interface EmployeeProjection {
Boolean getIsDeleted();
Boolean getIsEnabled();
String getCode();
String getName();
/*
* ...
*/
}
EmployeeRepository.java
package xxx.repository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import xxx.model.Employee;
import xxx.model.projection.EmployeeProjection;
import xxx.repository.custom.EmployeeRepositoryCustom;
#RepositoryRestResource(collectionResourceRel = "employee", path = "employees", excerptProjection = EmployeeProjection.class)
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long>, EmployeeRepositoryCustom {}
Thanks in advance.
Edit: added missing classes.
As mentioned before, one of the things I tried was to add an Employee list inside the Company object, which implies using Company's repository instead of the Employee's one to save both objects, but the other object was also arriving null. However, my colleague found out that, by using exported = false inside #RepositoryRestResource(), the value would be received correctly.
That would screw other things up, so we found the following temporary solution:
Create an exported = false repository (EmployeeWrapper) for the sole purpose of delivering the necessary Employee data to construct a new one inside save.
Instead of adding an Employee list inside Company, add an EmployeeWrapper list.
EmployeeWrapper also references Company.
We're still working on a more correct approach.
Update: a more correct approach:
My colleague also found out that, by adding a #Transient Employee list to Company, it's possible to receive the correctly filled out Employee object to save it. I don't know if it works at the repository since, due to other constraints, we moved to use a #RepositoryRestController and are receiving the Company as #RequestBody org.springframework.hateoas.Resource<Company> resource.
We still want to find a better solution, because an Employee list inside Company wasn't planned in our model and, worse yet, we're needing to use list of other things for other methods.
Update: an even better approach:
Experimenting a little more, we created a POJO containing the entities that we needed and received that in the controller, same way as before. Works well.
We're still not satisfied, though. Ideally, we want to receive the Employee to be saved, with the Company to be saved inside it, and save them both at once.