Spring Boot and JpaRepository sort by distance with latitude and longitude params - postgresql

I have a problem creating a query using the JpaRepository interface. I use the Pageable mechanism. How can I add sort by distance? Geographic parameters available at Event Place. (Latitude, longitude). I would like to add also the ability to limit distance by range. E.g:
"Where distance <: range".
I would like the distance to be returned in the "distance" field, which is #Transient.
EventRepository
#Repository
public interface EventRepository extends JpaRepository<Event, Long> {
public static final String GET_PAGE_QUERY = "WHERE " +
"(:searchTerm is null or e.title like '%:searchTerm%') AND " +
"((:userListId) is null or e.user.id in (:userListId)) AND " +
"((:sportTypeIdList) is null or e.sportType.id in (:sportTypeIdList)) " ;
#Query(value = "SELECT e FROM Event e " + GET_PAGE_QUERY,
countQuery = "SELECT COUNT(e) FROM Event e " + GET_PAGE_QUERY)
Page<Event> getPage(#Param("searchTerm") String searchTerm, #Param("userListId") List<Long> userListId, #Param("sportTypeIdList") List<Long> sportTypeIdList, Pageable pageable);
}
Event
#Getter
#Setter
#Entity
#Table(name = "event")
public class Event {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "event_seq")
#SequenceGenerator(name = "event_seq", sequenceName = "event_seq", allocationSize = 1)
private Long id;
#Column(name = "TITLE", length = 50, nullable = false)
#NotNull
#Size(min = 4, max = 50)
private String title;
#Column(name = "DESCRIPTION", length = 500, nullable = false)
#NotNull
#Size(min = 4, max = 500)
private String description;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
private SportType sportType;
#OneToOne(fetch = FetchType.EAGER, optional = false)
private EventPlace place;
#ManyToOne(optional = false)
private User user;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ")
#Column(name = "CREATE_DATE", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
#NotNull
private Date createDate = new Date();
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ")
#Column(name = "START_DATE", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
#NotNull
private Date startDate;
#Column(name = "STATUS", nullable = false)
#NotNull
#Enumerated(EnumType.STRING)
private EventStatus status = EventStatus.ACTIVE;
#Transient
private double distance;
}
EventPlace
#Getter
#Setter
#Entity
public class EventPlace {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "event_place_seq")
#SequenceGenerator(name = "event_place_seq", sequenceName = "event_place_seq", allocationSize = 1)
private Long id;
#Column(name = "NAME", length = 50, nullable = false)
#NotNull
#Size(min = 4, max = 50)
private String name;
#Column(name = "CITY", length = 50, nullable = false)
#NotNull
#Size(min = 4, max = 50)
private String city;
#Column(name = "STREET", length = 50, nullable = false)
#NotNull
#Size(min = 4, max = 50)
private String street;
#Column(name = "ZIP_CODE", length = 50, nullable = false)
#NotNull
#Size(min = 4, max = 50)
private String zipCode;
#Column(name = "DESCRIPTION", length = 500, nullable = false)
#NotNull
#Size(min = 4, max = 500)
private String description;
#Column(name = "LATITUDE", nullable = false)
#NotNull
private Double latitude;
#Column(name = "LONGITUDE", nullable = false)
#NotNull
private Double longitude;
}

Related

Spring Data JPA and cast to a Postgres custom domain

I have the query below:
#Query(value="select t.* from titulos t " +
"where (CAST(:modelosNota AS modelo_nota_fiscal) IS NULL OR t.modelo in (:modelosNota)) order by t.vencimento", nativeQuery = true)
List<Titulo> relatorioTitulosPagarReceber(#Param("modelosNota") String[] modelosNota);
But when it is executed I get the exception below:
org.postgresql.util.PSQLException: ERROR: operator does not exist: modelo_nota_fiscal = bytea
Dica: No operator matches the given name and argument type(s). You might need to add explicit type casts.
I think the message is not very accurate because I already have the type cast to modelo_nota_fiscal which is as custom domain type with a check constraint for some values.
When I run the query in PGAdmin it executes normally:
select t.* from titulos t where (CAST(null AS modelo_nota_fiscal) IS NULL OR t.modelo in (null)) order by t.vencimento;
Or this one that works just fine:
select t.* from titulos t where (CAST('NFe' AS modelo_nota_fiscal) IS NULL OR t.modelo in ('NFe')) order by t.vencimento;
My entity class as requested:
#Data
#EqualsAndHashCode(callSuper = false)
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Entity
#Table(name = "titulos")
public class Titulo extends AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(length = 30)
private String numero;
#Basic(optional = false)
#Column(nullable = false)
private short natureza;
#Basic(optional = false)
#Column(nullable = false)
private short origem;
#Basic(optional = false)
#Column(nullable = false, precision = 20, scale = 4)
private BigDecimal valor;
#Transient
#Column(name = "valorbaixado")
private BigDecimal valorBaixado;
#Transient
#Column(name = "saldoliquidotitulo")
private BigDecimal saldoLiquidoTitulo;
#Transient
#Column(name = "saldobrutotitulo")
private BigDecimal saldoBrutoTitulo;
#Basic(optional = false)
#Column(nullable = false, precision = 20, scale = 4)
private BigDecimal acrescimo;
#Basic(optional = false)
#Column(nullable = false, precision = 20, scale = 4)
private BigDecimal desconto;
#Basic(optional = false)
#Column(nullable = false, precision = 20, scale = 4)
private BigDecimal juros;
#Basic(optional = false)
#Column(nullable = false, precision = 20, scale = 4)
private BigDecimal multa;
#Basic(optional = false)
#Column(nullable = false)
private LocalDate emissao;
#Basic(optional = false)
#Column(nullable = false)
private LocalDate vencimento;
#Basic(optional = false)
#Column(name = "datamulta")
private LocalDate dataMulta;
#Basic(optional = false)
#Column(nullable = false)
private short situacao;
#Column(length = 2147483647)
private String observacao;
#Column(length = 2147483647)
private String historico;
#Column(length = 20, name = "documentovinculado")
private String documentoVinculado;
#Column(name = "parcela")
private Integer parcela;
#Column(name = "totalparcelas")
private Integer totalParcelas;
#Column(name = "modelo")
private String modelo;
#Column(name = "numerodocumentofiscal")
private String numeroDocumentoFiscal;
#Column(name = "seriedocumentofiscal")
private String serieDocumentoFiscal;
#Column(name = "subseriedocumentofiscal")
private String subserieDocumentoFiscal;
#Column(name = "cfop_codigo")
private String cfopCodigo;
#Column(name = "pis_retido", precision = 20, scale = 2)
private BigDecimal pisRetido;
#Column(name = "cofins_retido ", precision = 20, scale = 2)
private BigDecimal cofinsRetido;
#Column(name = "csll_retido ", precision = 20, scale = 2)
private BigDecimal csllRetido;
#Column(name = "irrf_retido ", precision = 20, scale = 2)
private BigDecimal irrfRetido;
#Column(name = "inss_retido ", precision = 20, scale = 2)
private BigDecimal inssRetido;
#Column(name = "iss_retido ", precision = 20, scale = 2)
private BigDecimal issRetido;
#Column(name = "valor_base ", precision = 20, scale = 2)
private BigDecimal valorBase;
#Column(name = "competencia")
private LocalDate competencia;
#Column(name = "linhadigitavelboleto", length = 44)
private String linhaDigitavelBoleto;
}
Here's my Postgre custom domain type:
CREATE DOMAIN public.modelo_nota_fiscal
AS character varying(3)
COLLATE pg_catalog."default"
CONSTRAINT check_modelo_nota_fiscal CHECK (VALUE::text = 'NFs'::text OR VALUE::text = 'NFe'::text OR VALUE::text = 'CEE'::text OR VALUE::text = 'CFG'::text OR VALUE::text = 'CFA'::text);
ALTER DOMAIN public.modelo_nota_fiscal
OWNER TO postgres;
COMMENT ON DOMAIN public.modelo_nota_fiscal
IS 'MODELO NOTA FISCAL: (NFs) Nota Fiscal de Serviço, (NFe) Nota Fiscal Eletrônica, (CEE) Conta de Energia Elétrica, (CFG) Conta de Fornecimento de Gás, (CFA) Conta de Fornecimento de Água.';
Does anyone know how am I supposed to cast to a custom Postgres domain using Spring Data JPA?

How to filter by faculty name?

I want to implement lazy record loading on a Primefaces DataTable (version 7). I have two entities, one is called Faculties and the other is Careers, which are related. The datatable correctly shows the list of all the races (includes pagination and filtering), the problem I have is that I do not know how to filter the races by the name of a certain faculty, since I do not know how to include the join in the query that I leave then.
Could you guide me on how to solve it please?
Entity Faculties
public class Facultades implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idfacultad")
private Integer idfacultad;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 100)
#Column(name = "nombre")
private String nombre;
#Size(max = 20)
#Column(name = "abreviatura")
private String abreviatura;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idfacultad")
private List<Carreras> carrerasList;}
Entity Carreras
public class Carreras implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idcarrera")
private Integer idcarrera;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 150)
#Column(name = "nombre")
private String nombre;
#Basic(optional = false)
#NotNull
#Column(name = "tipo")
private int tipo;
#JoinColumn(name = "idfacultad", referencedColumnName = "idfacultad")
#ManyToOne(optional = false)
private Facultades idfacultad;}
Query findByParams
public List<Carreras> findByParams(int start, int size, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Carreras> criteriaQuery = criteriaBuilder.createQuery(Carreras.class);
Root<Carreras> root = criteriaQuery.from(Carreras.class);
CriteriaQuery<Carreras> select = criteriaQuery.select(root);
Join<Carreras, Facultades> facultad = root.join("idfacultad");
if (sortField != null) {
criteriaQuery.orderBy(sortOrder == SortOrder.DESCENDING ? criteriaBuilder.asc(root.get(sortField)) : criteriaBuilder.desc(root.get(sortField)));
}
if (filters != null && filters.size() > 0) {
List<Predicate> predicados = new ArrayList<>();
filters.entrySet().forEach((entry) -> {
String key = entry.getKey();
Object val = entry.getValue();
if (!(val == null)) {
// Construimos la expresion con los predicados que si existan
Expression<String> expresion = root.get(key).as(String.class);
Predicate predicado = criteriaBuilder.like(criteriaBuilder.lower(expresion), "%" + val.toString().toLowerCase() + "%");
predicados.add(predicado);
}
});
if (predicados.size() > 0) {
criteriaQuery.where(criteriaBuilder.and(predicados.toArray(new Predicate[predicados.size()])));
}
}
// Creamos la consulta
TypedQuery<Carreras> consulta = em.createQuery(select);
consulta.setFirstResult(start);
consulta.setMaxResults(size);
return consulta.getResultList();
}
You need to manually check if the filter key equals the Facultades object, and in that case create a predicate on the joined expression that you have already created:
if (key.equals("Facultad")) {
expresion = facultad.get("nombre").as(String.class);
} else {
expresion = root.get(key).as(String.class);
}

Bean validation succeeded but failed on jpa merge method

I want to persist an entity(MyEntity) with merge method. This entity have some beans validation.
public class MyEntity extends AbstractEntity {
#Basic(optional = false)
#Column(name = "city", length = 255, nullable = false)
#NotNull
#NotEmpty(message = "{myentity.validation.size.name}")
private String city;
private String number;
#Basic(optional = false)
#Column(name = "zipcode", length = 255, nullable = false)
#NotNull
private String zipcode;
private String phoneNumber;
#Email(message = "{myentity.validation.conform.email}")
#Size(min = 2, max = 100, message = "{myentity.validation.size.email}")
private String email;
private String website;
private String gpsLocation;
#ElementCollection()
#CollectionTable(name = "translation_poi", joinColumns = #JoinColumn(name = "point_id"))
#MapKeyJoinColumn(name = "locale")
#NotEmpty
private Map<Locale, MyEntityI18n> translations = new HashMap<>();
}
#Embeddable
public class MyEntityI18n implements java.io.Serializable {
#Basic(optional = false)
#Column(name = "name", length = 255, nullable = false)
#NotNull
#NotEmpty(message = "{myentity.validation.size.name}")
private String name;
#Column(name = "comment", length = 1200)
private String comment;
#Column(name = "short_description", length = 1200)
private String shortDescription;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The merge succeeded on an existing entity value but with a new entity the merge failed despite the fact that the following validation succeeded.
private boolean validate(MyEntity poi) {
boolean result = true;
Set<ConstraintViolation<MyEntity>> constraintViolations = validator.validate(poi);
if (constraintViolations.size() > 0) {
result = false;
for (ConstraintViolation<MyEntity> constraints : constraintViolations) {
FacesContext context = FacesContext.getCurrentInstance();
String message = constraints.getPropertyPath() + " " + constraints.getMessage();
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, constraints.getMessage(), message));
}
}
return result;
}
Try to add a #Valid to MyEntity.translations property. I think that your validation method hasn't take account the MyEntityI18n.name validation.
About merge fails, Do you have a not-null DB constraint on the MyEntityI18n.name field?
Good luck!

Entity mapping issues

I am pretty new to java and data entities and I am getting the, "Exception Description: An incompatible mapping has been encountered between [class com.store.Product] and [class com.store.Cart]." error and not sure why (obviously).
My mappings are as follows:
Product.java
#Basic(optional = false)
#NotNull
#Column(name = "P_QTY")
private Integer pQty;
#Column(name = "P_PRICE")
private BigDecimal pPrice;
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "P_ID")
private String pId;
#Size(max = 10)
#Column(name = "P_NAME")
private String pName;
#Size(max = 20)
#Column(name = "P_DESC")
private String pDesc;
#OneToMany(mappedBy = "pId")
Cart.java
#Id
#Basic(optional = false)
#NotNull
#Column(name = "C_ID")
private String cId;
#Basic(optional = false)
#NotNull
#Column(name = "C_QTY")
private Integer cQty;
#Column(name = "C_PRICE")
private Double cPrice;
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 10)
#JoinColumn(name = "P_ID", referencedColumnName = "P_ID")
#ManyToOne
private String pId;
Any ideas? Like I said, I'm new to data entities and have done enough reading to confuse myself at this point so I apologize for any and all idiocy on my part.
Thanks in advance.

Insert object with composite primary key

I need help with this. With code is more clear, this is my function to persist:
public String finalizarCompra() {
Pedido pedido = new Pedido();
pedido.setEstado("almacen");
pedido.setFechaVenta(new Date());
pedido.setIdUsuario(loginBean.getUsuario());
Producto p;
Integer i;
DetPedido detPedido;
List<DetPedido> lista = new ArrayList<>();
for (Map.Entry e : productos.entrySet()) {
detPedido = new DetPedido();
p = (Producto) e.getKey();
i = (Integer) e.getValue();
detPedido.setProducto(p);
detPedido.setCantidad(i);
detPedido.setPrecioUnidad(p.getPrecioUnidad());
detPedido.setPedido(pedido);
lista.add(detPedido);
detPedidoBean.insert(detPedido);
}
pedido.setDetPedidoCollection(lista);
pedidoBean.insert(pedido);
return "";
}
This is my Pedido Entity:
#Entity
public class Pedido implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID_PEDIDO")
private Integer idPedido;
#Basic(optional = false)
#NotNull
#Column(name = "FECHA_VENTA")
#Temporal(TemporalType.TIMESTAMP)
private Date fechaVenta;
#Column(name = "FECHA_ENVIO")
#Temporal(TemporalType.TIMESTAMP)
private Date fechaEnvio;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 50)
#Column(name = "ESTADO")
private String estado;
#JoinColumn(name = "ID_USUARIO", referencedColumnName = "ID_USUARIO")
#ManyToOne(optional = false)
private Usuario idUsuario;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "pedido")
private Collection<DetPedido> detPedidoCollection;
// Getters and Setters //
This is my DetPedido Entity:
#Entity
public class DetPedido implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected DetPedidoPK detPedidoPK;
#Basic(optional = false)
#NotNull
#Column(name = "CANTIDAD")
private Integer cantidad;
#Basic(optional = false)
#NotNull
#Column(name = "PRECIO_UNIDAD")
private Double precioUnidad;
#JoinColumn(name = "ID_PRODUCTO", referencedColumnName = "ID_PRODUCTO", insertable = false, updatable = false)
#ManyToOne(optional = false)
private Producto producto;
#JoinColumn(name = "ID_PEDIDO", referencedColumnName = "ID_PEDIDO", insertable = false, updatable = false)
#ManyToOne(optional = false)
private Pedido pedido;
// Getters and Setters //
And this is my DetPedidoPK:
#Embeddable
public class DetPedidoPK implements Serializable {
#Basic(optional = false)
#NotNull
#Column(name = "ID_PEDIDO")
private Integer idPedido;
#Basic(optional = false)
#NotNull
#Column(name = "ID_PRODUCTO")
private Integer idProducto;
// Getters and Setters //
The Entities ara generated automatically from the Database, also DetPedidoPK, and now I don't know how to save a Pedido. I tried with the code above, but it doesn't work.
Can anybody help me?
Greetings.
If you are using JPA 1.0 and this entity model, then you will need to persist and flush both Producto and Pedido instances to have their IDs assigned before you can persist the DetPedido instance that will reference them. Once that is done, you will need to manually set the id values in DetPedido's DetPedidoPK instance so that they match the referenced Producto and DetPedido key values. You cannot insert DetPedido without the DetPedidoPK values having been set.
JPA 2.0 supports derived IDs, which allows marking the relationship as either #ID or #MapsId, indicating that the ID values should be pulled from the joincolumn associated to the relationship. In this case, it would become:
#ManyToOne(optional = false)
#MapsId("idProducto")
private Producto producto;
#ManyToOne(optional = false)
#MapsId("idPedido")
private Pedido pedido;
If you wanted, you could do away with the embeddable within DetPedido and just mark the relationships as the #Id, and because it is composite you would use the DetPedidoPK as the PK class.