问题
I have this generic CRUD (Controller, Repository, Service, Specification) with multiple entities that use @OneToMany annotations. The project only starts when I comment on these annotations, otherwise I get an error that always indicates a different "DAO" file.
Struchture:
oga-servicio-principal
-src/main/java
--com.oga.springboot
---app.principal
----controllers
----crud (My Generic CRUD files)
-----controllers
-----models
------dao
------dto
------entity
------service
------specifications
----models
-----dao
-----dto
-----entity
-----service
---OgaServicioPrincipalApplication
The Console log
error log
Generic Controller:
//imports...
public class MyCrudController<T> {
private IMyCrudService<T> service;
@Setter
protected T entity;
public MyCrudController(IMyCrudService<T> service) {
this.service = service;
}
@GetMapping
public List<T> findAll(
@RequestParam(value= "by", required = false) String prop,
@RequestParam(value= "val", required = false) String value,
@RequestParam(value= "sortby", required = false) String sort,
@RequestParam(value= "sortdir", required = false) String dir
) {
if(prop != null && value != null) {
if(sort != null) {
if(dir != null)
return service.findBySortDir(prop, ":", value, sort, dir);
else
return service.findBySort(prop, ":", value, sort);
}
else
return service.findBy(prop, ":", value);
}
else {
if(sort != null) {
if(dir != null)
return service.findAllSortDir(sort, dir);
else
return service.findAllSort(sort);
}
}
return service.findAll();
}
@GetMapping("/{id}")
public T findById(@PathVariable Integer id) {
T entidadDb = service.findById(id);
if (entidadDb == null)
throw new ResponseStatusException(HttpStatus.NOT_FOUND, entity.getClass().getSimpleName() + " inexistente");
else
return entidadDb;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public T create(@RequestBody T ent) {
return service.save(ent);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public void deleteById(@PathVariable Integer id) {
Boolean existe = service.existsById(id);
if (existe == true)
service.deleteById(id);
else
throw new ResponseStatusException(HttpStatus.NOT_FOUND, entity.getClass().getSimpleName() + " inexistente");
}
@PutMapping("/{id}")
@ResponseStatus(HttpStatus.CREATED)
public T update(@RequestBody T entidad, @PathVariable Integer id) {
T entidadDb = service.findById(id);
if (entidadDb == null)
throw new ResponseStatusException(HttpStatus.NOT_FOUND, entity.getClass().getSimpleName() + " inexistente");
else {
BeanUtils.copyProperties(entidad, entidadDb, "id");
return service.save(entidadDb);
}
}
}
Generic DAO
package com.oga.springboot.app.principal.crud.models.dao;
//import
@NoRepositoryBean
public interface IMyCrudDao<T> extends PagingAndSortingRepository<T, Integer>, JpaSpecificationExecutor<T>{}
Generic Service Interface
package com.oga.springboot.app.principal.crud.models.service;
import java.util.List;
public interface IMyCrudService<T> {
public List<T> findAll();
public List<T> findAllSort(String sortField);
public List<T> findAllSortDir(String sortField, String direction);
public List<T> findBySort(String prop, String op, String value, String sortField);
public List<T> findBySortDir(String prop, String op, String value, String sortField, String direction);
public T findById(Integer id);
public List<T> findBy(String prop, String op, String value);
public T save(T entity);
public void deleteById(Integer id);
public boolean existsById(Integer id);
}
Generic Service Implementation
package com.oga.springboot.app.principal.crud.models.service;
// imports...
@Service
public class MyCrudServiceImpl<T> implements IMyCrudService<T> {
@Setter
protected IMyCrudDao<T> dao;
@Override
@Transactional(readOnly = true)
public List<T> findAll() {
return (List<T>) dao.findAll();
}
@Override
public List<T> findAllSort(String sortField) {
return (List<T>) dao.findAll(Sort.by(sortField));
}
@Override
public List<T> findAllSortDir(String sortField, String direction) {
return (List<T>) dao.findAll(Sort.by(Sort.Direction.fromString(direction), sortField));
}
@Override
public List<T> findBy(String prop, String op, String value) {
MyCrudSpecification<T> spec = new MyCrudSpecification<T>(new SearchCriteria(prop, op, value));
return dao.findAll(spec);
}
@Override
public List<T> findBySort(String prop, String op, String value, String sortField) {
MyCrudSpecification<T> spec = new MyCrudSpecification<T>(new SearchCriteria(prop, op, value));
return dao.findAll(spec, Sort.by(sortField));
}
@Override
public List<T> findBySortDir(String prop, String op, String value, String sortField, String direction) {
MyCrudSpecification<T> spec = new MyCrudSpecification<T>(new SearchCriteria(prop, op, value));
return (List<T>) dao.findAll(spec, Sort.by(Sort.Direction.fromString(direction), sortField));
}
@Override
public T findById(Integer id) {
return dao.findById(id).orElse(null);
}
@Override
@Transactional
public T save(T entity) {
return dao.save(entity);
}
@Override
@Transactional
public void deleteById(Integer id) {
dao.deleteById(id);
}
@Override
@Transactional(readOnly = true)
public boolean existsById(Integer id) {
return dao.existsById(id);
}
}
Generic Specification
package com.oga.springboot.app.principal.crud.models.specifications;
//imports
@SuppressWarnings("serial")
public class MyCrudSpecification<T> implements Specification<T> {
private SearchCriteria criteria;
public MyCrudSpecification(SearchCriteria searchCriteria) {
this.criteria = searchCriteria;
}
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
if (criteria.getOperation().equalsIgnoreCase(">")) {
return criteriaBuilder.greaterThanOrEqualTo(root.<String>get(criteria.getKey()),
criteria.getValue().toString());
} else if (criteria.getOperation().equalsIgnoreCase("<")) {
return criteriaBuilder.lessThanOrEqualTo(root.<String>get(criteria.getKey()),
criteria.getValue().toString());
} else if (criteria.getOperation().equalsIgnoreCase(":")) {
if (root.get(criteria.getKey()).getJavaType() == String.class)
return criteriaBuilder.like(root.<String>get(criteria.getKey()), "%" + criteria.getValue() + "%");
else if (root.get(criteria.getKey()).getJavaType().isAnnotationPresent(Entity.class))
return criteriaBuilder.equal(root.get(criteria.getKey()).get("id"), criteria.getValue());
else
return criteriaBuilder.equal(root.get(criteria.getKey()), criteria.getValue());
}
return null;
}
}
Particular extends for Controller, Service, Dao
//AudienciaTipoController.java
package com.oga.springboot.app.principal.controllers;
//imports
@RestController
@RequestMapping("audiencia-tipo")
public class AudienciaTipoController extends MyCrudController<AudienciaTipo> {
@Autowired
public AudienciaTipoController(@Qualifier("audienciaTipoServiceImpl") IMyCrudService<AudienciaTipo> myService) {
super(myService);
AudienciaTipo entity = new AudienciaTipo();
this.setEntity(entity);
}
}
------------------
//IAudienciaTipoDao.java
package com.oga.springboot.app.principal.models.dao;
///imports
@Repository
public interface IAudienciaTipoDao extends IMyCrudDao<AudienciaTipo>{}
-------------------------
// IAudienciaTipoService.java
package com.oga.springboot.app.principal.models.service;
//imports
public interface IAudienciaTipoService extends IMyCrudService<AudienciaTipo>{}
--------------------------
// AudienciaTipoServiceImpl.java
package com.oga.springboot.app.principal.models.service;
//imports
@Service
public class AudienciaTipoServiceImpl extends MyCrudServiceImpl<AudienciaTipo> implements IAudienciaTipoService{
@Autowired
public AudienciaTipoServiceImpl(IAudienciaTipoDao dao) {
super();
this.setDao(dao);
}
}
Some Entities
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.oga.springboot.app.principal.models.entity;
//imports
@Entity
@Table(name = "audiencia_tipos", uniqueConstraints = {
@UniqueConstraint(columnNames = {"descripcion"}),
@UniqueConstraint(columnNames = {"invenietid"})})
@NamedQueries({
@NamedQuery(name = "AudienciaTipo.findAll", query = "SELECT a FROM AudienciaTipo a")})
public class AudienciaTipo implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(nullable = false)
private Integer id;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 250)
@Column(nullable = false, length = 250)
private String descripcion;
@Basic(optional = false)
@NotNull
@Column(nullable = false)
private short tiempo;
@Basic(optional = false)
@NotNull
@Column(nullable = false)
private short activo;
private Integer invenietid;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "tipoid")
private List<JuezTipoAudiencia> juezTipoAudienciasList;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "audienciaTipoid")
private List<AudienciaTipoAudiencia> audienciaTipoAudienciasList;
@JoinColumn(name = "categoriaid", referencedColumnName = "id")
@ManyToOne
private Categoria categoriaid;
@JoinColumn(name = "prioridadid", referencedColumnName = "id", nullable = false)
@ManyToOne(optional = false)
private Prioridad prioridadid;
@JoinColumn(name = "usuarioid", referencedColumnName = "id", nullable = false)
@ManyToOne(optional = false)
private Usuario usuarioid;
public AudienciaTipo() {
}
public AudienciaTipo(Integer id) {
this.id = id;
}
public AudienciaTipo(Integer id, String descripcion, short tiempo, short activo) {
this.id = id;
this.descripcion = descripcion;
this.tiempo = tiempo;
this.activo = activo;
}
//Getter and Setters
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
System.out.println("<--- EQUALS ---->");
System.out.println(this.toString());
if (!(object instanceof AudienciaTipo)) {
return false;
}
AudienciaTipo other = (AudienciaTipo) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "com.mycompany.inversaoga.AudienciaTipos[ id=" + id + " ]";
}
}
package com.oga.springboot.app.principal.models.entity;
//imports...
@Entity
@Table(name = "juez_tipo_audiencias")
@NamedQueries({
@NamedQuery(name = "JuezTipoAudiencia.findAll", query = "SELECT j FROM JuezTipoAudiencia j")})
public class JuezTipoAudiencia implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(nullable = false)
private Integer id;
@JoinColumn(name = "tipoid", referencedColumnName = "id", nullable = false)
@ManyToOne(optional = false)
private AudienciaTipo tipoid;
@JoinColumn(name = "juezid", referencedColumnName = "id", nullable = false)
@ManyToOne(optional = false)
private Juez juezid;
public JuezTipoAudiencia() {
}
public JuezTipoAudiencia(Integer id) {
this.id = id;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof JuezTipoAudiencia)) {
return false;
}
JuezTipoAudiencia other = (JuezTipoAudiencia) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "com.mycompany.inversaoga.JuezTipoAudiencias[ id=" + id + " ]";
}
}
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.oga.springboot.app.principal</groupId>
<artifactId>oga-servicio-principal</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>oga-servicio-principal</name>
<description>principal</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
</properties>
<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-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
OgaServicioPrincipalApplication.java
package com.oga.springboot;
//imports...
@SpringBootApplication
@EnableEurekaClient
public class OgaServicioPrincipalApplication {
public static void main(String[] args) {
SpringApplication.run(OgaServicioPrincipalApplication.class, args);
}
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}
application.properties
spring.application.name=oga-servicio-principal
server.port=8091
eureka.instance.instance-id=${spring.application.name}:${spring.application.instance_id:${random.value}}
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
spring.datasource.url=jdbc:postgresql://localhost:5432/database_name
spring.datasource.username=user
spring.datasource.password=password
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL95Dialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true
logging.level.org.hibernate.SQL=debug
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 20000
ribbon.ConnectTimeout: 3000
ribbon.ReadTimeout: 10000
来源:https://stackoverflow.com/questions/63166540/problem-with-onetomany-relations-in-a-generic-crud