I want to have a simple parent children relationship but somehow it does not work and I don't get what is missing.
Parent Mapper Interface (adding uses = {LayerMapper.class} does not change anything):
#Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
#DecoratedWith(MlpConfigMapperDecorator.class)
public interface MlpConfigMapper {
#Mapping(target = "epochNumber", source = "epochs")
#Mapping(target = "activationFunction", ignore = true)
MlpConfig toEntity(CustomMlpConfigRequest mlpConfigDto);
}
Parent decorator according to this answer (https://stackoverflow.com/a/60217018/10565504):
public abstract class MlpConfigMapperDecorator implements MlpConfigMapper {
#Autowired
#Qualifier("delegate")
private MlpConfigMapper delegate;
#Autowired
private ActivationFunctionService activationFunctionService;
#Override
public MlpConfig toEntity(CustomMlpConfigRequest mlpConfigDto) {
MlpConfig mlpConfig = delegate.toEntity(mlpConfigDto);
mlpConfig.setActivationFunction(activationFunctionService.findByType(mlpConfigDto.getActivationFunction()));
return mlpConfig;
}
}
The Parent DTO:
public class CustomMlpConfigRequest {
private String name;
private String description;
private int batchSize;
private int epochs;
private List<LayerDto> layers;
private String activationFunction;
}
The Child DTO:
public class LayerDto {
public String type;
public int orderNumber;
public int neuronsNumber;
}
Parent Entity:
public class MlpConfig {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private String description;
private int batchSize;
private int epochNumber;
#JoinColumn(nullable = false, name = "activationFunction_id")
#ManyToOne(fetch = FetchType.LAZY)
private ActivationFunction activationFunction;
#JsonManagedReference
#Column(nullable = false)
#OneToMany(mappedBy = "mlpConfig", cascade = CascadeType.ALL)
private List<Layer> layers;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
private Date lastUpdated;
}
Child Entity:
public class Layer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private ELayer type;
private int neuronsNumber;
private int orderNumber;
#EqualsAndHashCode.Exclude
#ToString.Exclude
#ManyToOne(fetch = FetchType.LAZY, optional=false)
#JoinColumn(name = "mlpConfig_id", nullable=false)
#JsonBackReference
private MlpConfig mlpConfig;
}
Generated Child Entity Mapper Method (setChildren or setMlpConfig() in my case is missing):
#Override
public LayerDto layerToDto(Layer layer) {
if ( layer == null ) {
return null;
}
LayerDto layerDto = new LayerDto();
if ( layer.getType() != null ) {
layerDto.setType( layer.getType().name() );
}
layerDto.setOrderNumber( layer.getOrderNumber() );
layerDto.setNeuronsNumber( layer.getNeuronsNumber() );
return layerDto;
}
How do I get the mapper to set the parent in the child?
do you have Layer toEntity(LayerDto layerDto);
Also, on setter of layers on entity class, you should say, layers.forEach (layer -> layer.setmlpConfig(this));
Did you do that setting job? If you don't, layers of mplConfig entity can always be null when you try to get it.
In the end I fixed it myself. I don't know if it is best practice and probably the mapper should do it without manual help but at least it works:
public abstract class MlpConfigMapperDecorator implements MlpConfigMapper {
#Autowired
#Qualifier("delegate")
private MlpConfigMapper delegate;
#Autowired
private ActivationFunctionService activationFunctionService;
#Override
public MlpConfig mlpConfigToEntity(CustomMlpConfigRequest mlpConfigDto) {
MlpConfig mlpConfig = delegate.mlpConfigToEntity(mlpConfigDto);
mlpConfig.setActivationFunction(activationFunctionService.findByType(mlpConfigDto.getActivationFunction()));
//this is the difference. I set the config for the layer manually
mlpConfig.getLayers().forEach(e -> e.setMlpConfig(mlpConfig));
return mlpConfig;
}
}
Related
#Mapper(componentModel = "spring")
public interface ApiBeanMapper {
ApiBeanMapper mapper = Mappers.getMapper(ApiBeanMapper.class);
ApiEntity apiModelToEntity(Api user);
Api apiEntityToModel(ApiEntity user);
List<Api> apiEntityToModel(List<ApiEntity> user);
}
Trying to find a good junit5 test using Jacoco coverage report.Unable to get coverage on ApiBeanMapper mapper= Mappers.getMapper(ApiBeanMapper.class);
Api
public class Api {
private long id;
private String name;
private List<ApiResponseField> fields;
}
ApiEntity
public class ApiEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#Column(name = "privilege_name")
private String privilegeName;
#OneToMany(mappedBy = "api", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<ApiResponseFieldEntity> apiResponseFields;
#Column(name = "field_access")
private boolean fieldAccess;
#Column(name = "field_mapping")
private boolean fieldMapping;
}
In my application, i have these three classes
#Entity
#Indexed
public class CpamEntity {
#Id
private String externalId;
#Field
private String cgr;
#ElementCollection
private Set<AttachmentEntity> attachments;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "externalId")
private Set<DataEntity> data;
}
#Entity
#Indexed
public class DataEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String internalId;
#Field
private String number;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "externalId", updatable = false)
private FrameworkEntity frameworkEntity;
}
the below code is to insert DataEntity (X) into CpamEntity, then get all attachments CpamEntity by IdChild and insert the same DataEntity (X) in them
the problem is the first insertion doesn't happen, and the attachments CpamEntity insered
public void on(Event event) {
CpamEntity cpamEntity = getCpamEntity(event.externalId);
if (cpamEntity != null) {
Set<DataEntity> datas = cpamEntity.getData();
datas.add(new DataEntity(event.internalId, event.number));
cpamEntity.setData(datas);
repository.save(cpamEntity); // not saved
if (CollectionUtils.isNotEmpty(cpamEntity.getAttachments())) {
cpamEntity.getAttachments().forEach(attachment -> {
cpamRepositoryentity = repository.findByCgr(attachment.getIdChild());
if (entity != null) {
Set<Entity> dataEntities = entity.getData();
dataEntities.add(new DataEntity(event.internalId, event.number));
entity.setData(dataEntities);
cpamRepository.save(entity); // saved
}
});
}
}
}
I'm doing a registration and I have the fields
Nome:
Data de Nascimento:
Inscrição Estadual:
Nome Responsável:
CPF Responsável:
Cep:
Bloco:
Número:
when i saving, I can not write data from the PessoasEnderecos class, the other data is recording normal. I'm getting all the data on the screen so much that I debugged the browser to see ..
It shows no error. Does anyone know what I'm missing ??
my class Pacientes
#Entity
#Table(name = "pacientes", schema = "sau")
public class Pacientes implements Serializable {
private static final long serialVersionUID = 5776384003601026304L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idPaciente")
private Long idPaciente;
#JoinColumn(name="idPessoa")
#ManyToOne(cascade = CascadeType.ALL)
private Pessoas pessoa;
#Column(name = "nomeResponsavel")
private String nomeResponsavel;
#Column(name = "cpfResponsavel")
private String cpfResponsavel;
public Pacientes() {
}
//gets and sets
}
my class Pessoas
#Entity
#Table(name = "pessoas", schema="glb")
public class Pessoas implements Serializable {
private static final long serialVersionUID = -4042023941980758267L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
private Long idPessoa;
#Temporal(TemporalType.DATE)
private Date dataNascimento;
private String inscricaoEstadual;
private String inscricaoMunicipal;
private String nome;
public Pessoas() {
}
//gets and sets
}
#Entity
#Table(name = "pessoas_enderecos" ,schema="glb")
public class PessoasEnderecos implements Serializable {
private static final long serialVersionUID = -2560542418318988673L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idPessoaEndereco;
private String bloco;
private String cep;
private String numero;
#JoinColumn(name="idPessoa")
#ManyToOne(optional = false, cascade = CascadeType.ALL)
private Pessoas pessoa;
public PessoasEnderecos() {
}
//gets and sets
}
my methods
class Controller
#RequestMapping(method = RequestMethod.POST, value = "/pacientes")
public Pacientes cadastrarPacientes(#RequestBody Pacientes pac) {
return pacientesService.cadastrar(pac);
}
class service
public Pacientes cadastrar(Pacientes pacientes){
return pacRepository.save(pacientes);
}
class repository
public interface PacientesRepository extends JpaRepository<Pacientes, Integer> {
}
You should also add the linkage #OneToMany in Pacientes:
public class Pacientes implements Serializable {
...
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "pessoa")
#PrimaryKeyJoinColumn
private List<PessoasEnderecos> pessoasEnderecos = new ArrayList<>();
Update:
and your JSON should be something like this:
{
"nomeResponsavel": "abc",
"pessoasEnderecos": [
{
"bloco": "sdds",
"cep": "sdasdsad",
"numero": "sdasdsa"
}
]
}
I'm getting an error saying that my entity is detached when I try to persist it. When debugging I can see that the objects I try to save have an ID. I am guessing that this might be related to my annotations in JPA but I cannot figure out what is the cause of this problem.
Class with method that causes error(the last line of doStuff() is responsible):
#RestController
#RequestMapping("/test")
public class ConcreteMyController implements MyController {
private final CategoryService categoryService;
private final OrmFactory ormFactory;
private final SongService songService;
private final CategoryForSongService categoryForSongService;
#Autowired
public ConcreteMyController(CategoryService categoryService, OrmFactory ormFactory, SongService songService, CategoryForSongService categoryForSongService) {
this.categoryService = categoryService;
this.ormFactory = ormFactory;
this.songService = songService;
this.categoryForSongService = categoryForSongService;
}
#RequestMapping(method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.OK)
#Override
public void doStuff() {
String title = "BestSongInTheWorld";
String popCategoryName = "pop";
String rockCategoryName = "rock";
String jazzCategoryName = "jazz";
Song song = this.ormFactory.createSong(title, "3:14");
this.songService.save(song);
Set<Category> categories = Sets.newHashSet(new Category(popCategoryName), new Category(rockCategoryName), new Category(jazzCategoryName));
this.categoryService.save(categories);
Song retrievedSong = songService.get(title);
Set<Category> retrievedCategories = categoryService.get(Sets.newHashSet(popCategoryName, rockCategoryName));
CategoryForSong categoryForSong = new CategoryForSong(retrievedSong.getTitle(), retrievedCategories);
this.categoryForSongService.save(categoryForSong);
}
}
Entities:
#Entity
#Table
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "name")
private String name;
#Id
#GeneratedValue
private Long id;
}
#Entity
#Table
public class Song implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "duration")
private String duration;
#Column(name = "title")
private String title;
#Id
#GeneratedValue
private Long id;
}
#Entity
#Table
public class CategoryForSong implements Serializable {
private static final long serialVersionUID = 1L;
#JoinColumn(name = "categories")
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Category> categories;
#Column(name = "songTitle")
private String songTitle;
#Id
#GeneratedValue
private Long id;
}
Getters, setters, equals etc. has been omitted. The reason that CategoryForSong contains a string with a songTitle instead of an actual Song is just me trying to isolate the problem. That did not seem to help.
My services are not very interesting, so I will add the one for Song here just to show the structure:
#Service
public class ConcreteSongService implements SongService {
#Autowired
private SongRepository songRepository;
#Transactional
#Override
public void save(Song song) {
songRepository.save(song);
}
#Transactional
#Override
public Song get(String title) {
return songRepository.getByTitle(Sets.newHashSet(title)).stream().findFirst().get();
}
}
And the repository is as simple as this:
public interface SongRepository extends CrudRepository<Song, Long> {
Set<Song> getByTitle(Set<String> titles);
}
The exception looks like this:
org.hibernate.PersistentObjectException: detached entity passed to persist: proofofconcept.springmvc.model.orm.Category
Help would be very appreciated.
I have following JPA entity structure.
#Entity
#Table(name = "PARENT_DETAILS")
class Parent{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "parent_details_seq")
#SequenceGenerator(name = "parent_details_seq", sequenceName = "PARENT_DETAILS_SEQ", allocationSize = 1)
#Column(name = "PARENT_ID")
private long parentId;
#Column(name = "PARENT_NAME")
private String parentName;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "childPK.parent", cascade = CascadeType.ALL)
private Set<Child> child;
//setters and getters
}
#Entity
#Table(name = "CHILD_DETAILS")
public class Child {
private ChildPK childPK;
public void setProgramProcessesPK(ChildPK childPK) {
this.childPK = childPK;
}
#EmbeddedId
public ChildPK getChildPK() {
return childPK;
}
}
#Embeddable
public class ChildPK implements Serializable {
private static final long serialVersionUID = 1L;
private Parent parent;
private long childId;
#Column(name = "CHILDID")
public long getChildId() {
return childId;
}
public void setChildId(long childId) {
this.childId = childId;
}
#ManyToOne
#JoinColumn(name = "PARENT_ID", referencedColumnName = "PARENT_ID", nullable = false)
public ParentDetails getParent() {
return parent;
}
}
I want to write a JPA query which will return the PARENT_NAME and the count of all children for a given parent_id.
Tthe only solution I can think of is joining and writing a complex criteria query.
I cannot think of a way to get the result using a simple JPA query.
Is there an easier way to do this?
Have you tried SIZE? Something like "Select parent.parentName, Size(parent.child) from Parent parent" might work.
You can use JPA Named Query such as this:
private static class ParentChildsNumber {
public String parentName;
public Integer childsNum;
public ParentChildsNumber(String parentName, Integer childsNum) {
this.parentName = parentName;
this.childsNum = childsNum;
}
}
#NamedQuery(name="getParentChildsNumberQuery", query="SELECT NEW ParentChildsNumber(p.parentName, SIZE(p.child)) FROM Parent p WHERE p.parentId = :parentId GROUP BY p.parentId, p.parentName")
Use it in your code in the following way:
#PersistenceContext(unitName="YourPersistentUnit")
private EntityManager em;
em.createNamedQuery("getParentChildsNumberQuery", ParentChildsNumber.class).setParameter("parentId", parentId).getSingleResult();