Spring JpaRepository getOne() for table with PK as String - java

I am using Spring Boot with a JpaRepository. I have a table defined with a PK that is a String.
CREATE TABLE mytable (
uuid uuid DEFAULT gen_random_uuid() PRIMARY KEY,
url text NOT NULL,
status status DEFAULT 'pending' NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL
);
Question
In the JpaRepository, how do I do the equivalent of getOne(uuid)? (getOne(uuid) receives a paramater type Long) i.e. How do I retrieve one row for a uuid that is type String?
Code
#Entity
#Table(name = "mytable")
public class MyTableEntity {
public enum Status {
pending,
complete,
processing,
failed
}
#Id
#Column(name = "uuid")
//#GeneratedValue(strategy = GenerationType.AUTO)
private String uuid;
#Column(name = "url", nullable = false)
private String url;
#Enumerated(EnumType.STRING)
#Column(columnDefinition = "photo_status", nullable = false)
//#Type( type = "pgsql_enum" )
private Status status;
#Column(name = "created_at", nullable = false)
private LocalDateTime created_at;
Repostory
public interface MyRepository extends JpaRepository<MyTableEntity, Long> {
}

So if your entity has String key you can use such declaration of your Repository :
#Repository
public interface MyRepository extends JpaRepository<MyTableEntity, String> {
}
Now if you want to get entity by id, which is of type String, you can use for example :
private void someMethod() {
UUID uuid = UUID.randomUUID();
MyTableEntity myEntity = myRepository.getOne(uuid.toString());
}
If you look at JpaRepository definition it expects first type to be your Entity class, and second is type of the key of this entity :
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T>

Related

JPA, Hibernate: fields of subclasses are null with #MappedSuperclass and InheritanceType.JOINED strategy

I'm trying to persist a Person entity, but I keep getting this null constraint violation error:
org.postgresql.util.PSQLException: ERROR: null value in column "created_by_party_id" violates not-null constraint
Detail: Failing row contains (5023, John, null, Smith, null, 1, null, null, 2020-11-11 07:33:31.590766-05, null, null, null).
What I have tried:
With and without the Discriminator annotations
Setting the value in #PrePersist
Initializing createdById in the base class with a default value
Making the Party class instantiable (not abstract), and persisting it directly - THAT WORKS, but it's not what I need
For some reason, the createdById is null by the time the SQL gets generated and passed off to to PostgreSQL. (I have verified in debug mode that this field is set, on the person entity, when it gets passed to the DAO save call.)
I'm using Spring boot, Hibernate, and PostgreSQL to map my tables and classes like this:
#MappedSuperclass
#EntityListeners( value = { EntityAuditListener.class } )
public abstract class BaseJPA {
#Column(name = "created_by_party_id", nullable = false)
private Long createdById = 1l;
/* Getters and Setters ... */
}
public class EntityAuditListener {
#PrePersist
public void prePersist(BaseJPA jpa) {
jpa.setCreatedById( 1l );
}
}
The Party class, although abstract, maps to a PARTY table:
#Entity(name = "Party")
#Table(name = "PARTY")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "PARTY_TYPE_CODE", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Party extends BaseJPA implements Serializable {
private static final long serialVersionUID = 5434024967600745049L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PARTY_ID_SEQ")
#SequenceGenerator(name = "PARTY_ID_SEQ", sequenceName = "PARTY_ID_SEQ", allocationSize = 1)
#Column(name = "PARTY_ID")
protected Long partyId;
#Column(name = "PARTY_TYPE_CODE", insertable = false, updatable = false)
protected PartyType partyType;
/* Getters & Setters ... */
}
The PartyType enum is registered with an AttributeConverter, with #Converter(autoApply = true)
public enum PartyType {
PERSON(1), UNIT(2), SYSTEM(3);
private final int value;
PartyType(int value) {
this.value = value;
}
/* Getter */
}
#Entity(name = "Person")
#Table(name = "PERSON")
#DiscriminatorValue( "1" )
#Inheritance(strategy = InheritanceType.JOINED)
public class Person extends Party implements Serializable {
private static final long serialVersionUID = -5747077306637558893L;
#Column(name = "FIRST_NAME")
private String firstName;
#Column(name = "LAST_NAME")
private String lastName;
/* More fields ...*/
public Person() {
this.partyType = PartyType.PERSON;
}
/* Getters & Setters */
}
Are you sure that the entity listener method is called? Are you sure that there is no other method that might set the value to null?
If all that doesn't help, please create a reproducing test case and submit an issue to the Hibernate issue tracker.

How to build microservice GET api in springboot which can take single or multiple parameters as insert in Request url and fetch the data as request

GET/data/search? Will return all records
GET/data/search?name=jhon&add=US Will return specific records as per request
GET/data/search? Amount=100 Will return all records specific to amount 100
The client can enter at time above all or some parameters as per his requirements.
Any links to reference code will also do
below is my error and implementation
where my repo method is not getting called it is invoking the service method and printing the sysout of controller and service but not of repo that means it is not calling to repo below is the consol output:
Request param received (sysout)
Inside service method(sysout)
2019-10-15 13:54:30,185 ERROR [http-nio-8080-exec-1] org.apache.juli.logging.DirectJDKLog: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
#RestController
public class MobileController {
#GetMapping(value="/mobile/search")
public List<MobileResponse> getMobile(#RequestParam Map<String, String>
map) {
MobileService mobileService =new MobileService();
System.out.println("Request param received");
map.forEach((k,v)->System.out.println(k+":"+v));
List<MobileResponse> mobiles = mobileService.getAllMobiles(map);
mobiles.forEach(mobile-> System.out.println(mobile));
return mobiles;
}
}
service
#Service
public class MobileService {
public List<MobileResponse> getAllMobiles(Map<String, String> map) {
List<MobileResponse> mobilResponseList = new ArrayList<>();
System.out.println("Inside service method");
MobileRepoIntf repo=null;
mobilResponseList = repo.findAllMobiles(map);
return mobilResponseList;
}
}
Repo interface
#Repository
public interface MobileRepoIntf{
List<MobileResponse> findAllMobiles(Map<String, String> map);
}
repo implemantation
public class MobileRepository implements MobileRepoIntf {
#PersistenceContext
private EntityManager em;
#SuppressWarnings("uncheaked")
#Override
public List<MobileResponse> findAllMobiles(Map<String, String> map) {
System.out.println("inside Repo method ");
String query = "select m.id,h.id, r.id from
com.axiomtelecom.assignment.entities.Mobile m,
com.axiomtelecom.assignment.entities.Hardware
h,com.axiomtelecom.assignment.entities.Releases r where m.hardware_id =
h.id AND m.releases_id = r.id";
List<MobileResponse> mobileList = new ArrayList();
Query qry = em.createQuery(query);
System.out.println("Query is "+qry);
return qry.getResultList();
}
}
Mobile Entity
#Entity
#Table(name = "MOBILE")
public class Mobile implements Serializable {
//Logger logger = (Logger) LoggerFactory.getLogger(Mobile.class);
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "brand")
private String brand;
#Column(name = "phone")
private String phone;
#Column(name = "picture")
private String picture;
#Column(name = "sim")
private String sim;
#Column(name = "resolution")
private String resolution;
//#OneToOne(cascade = CascadeType.ALL)
#OneToOne
#JoinColumn(name ="id")
private Hardware hardware;
#OneToOne
#JoinColumn(name="id")
private Releases releases;
//followed by setter and getter methods
}
Hardware Entity
#Entity
#Table(name="Hardware")
public class Hardware implements Serializable {
//Logger logger = (Logger) LoggerFactory.getLogger(Hardware.class);
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="audioJacks")
private String audioJacks;
#Column(name = "gps")
private String gps;
#Column(name = "battery")
private String battery;
//followed by setter and getter methods
}
Releases entity
#Entity
#Table(name="Releases")
public class Releases implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="priceEuro")
private long priceEuro;
#Column(name="announceDate")
private String announceDate;
//followed by setter and getter methods
}
data.sql file
enter code here
drop table if exists Hardware;
CREATE table Hardware (
id INT AUTO_INCREMENT PRIMARY KEY,
audioJacks varchar(200),
gps varchar(100),
battery varchar(200),
);
drop table if exists Releases;
create table If Not exists Releases (
id INT AUTO_INCREMENT PRIMARY KEY,
priceEuro int,
announceDate varchar(100),
);
drop table if exists Mobile;
CREATE TABLE If Not exists Mobile (
id INT AUTO_INCREMENT PRIMARY KEY,
brand VARCHAR(250),
phone VARCHAR(250),
picture VARCHAR(250),
sim VARCHAR(250),
resolution VARCHAR(250),
hardware_id int references Hardware(id),
releases_id int references Releases(id)
);
//followed by insert query first for releases,hardware and then for mobile
data is inserted as expected in db.
There are multiple ways to achieve it.
You could receive the each query parameter as a method argument:
#GetMapping("/foo")
public ResponseEntity<Foo> getFoo(#RequestParam(value = "name", required = false) String name,
#RequestParam(value = "amount", required = false) Integer amount) {
...
}
You could receive all parameters as a Map<String, String>:
#GetMapping("/foo")
public ResponseEntity<Foo> getFoo(#RequestParam Map<String, String> parameters) {
...
}
Or you could define a class for representing the parameters and receive an instace of it as a method argument:
#Data
public class FooQueryParameters {
private String name;
private Integer amount;
}
#GetMapping("/foo")
public ResponseEntity<Foo> getFoo(FooQueryParameters parameters) {
...
}
It should be something like that:
#GetMapping("/data/search")
public YourObject getData(#RequestParam(value = "name", required = false) String name,
#RequestParam(value = "amount", required = false) Integer amount){
...
}

ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper [http-nio-8080-exec-3] (logExceptions:131) [] - The column name is not valid

trying to run a native query however I'm getting The column name sb_modelid is not valid. when attempting to map the return object to my model object in java? I have verified all the column names are correct.
1) Why is it referring to my column name as sb_modelid and not sbModelID?
2) Why is not being mapped to my POJO correctly?
Thanks!
Model Object:
package com.dish.wfm.linkingTickets.model.repository;
public class Model {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "sbModelID")
private Long sbModelId;
#Column(name = "modelID")
private String modelID;
#Column(name = "serialNumber")
private String serialNumber;
#Column(name = "serviceContractNumber")
private String serviceContractNumber;
}
Repo:
#Repository
public interface SBModelRepo extends JpaRepository<Model, Long> {
#Query(value = "select m.sbModelID, m.modelID, m.serialNumber, m.serviceContractNumber from sb.Model m where m.modelID = ?1 and m.serialNumber = ?2", nativeQuery = true)
Model findTopByModelIDAndSerialNumber(String modelID, String serialNumber);
}

Can i get data from multiple tables without jointables or foreign keys at them

I'm trying to get a single data from two tables of database. These tables doesn't have foreign keys and no jointables too. I'm using spring-data to retrieve required data for first data set.
I have two data sets that have a common String value, and want to retrieve data from both tables not using jointables or foreign keys, retrieving data from the first data set.
I'm using simple DataRepository interface
import org.springframework.data.jpa.repository.JpaRepository;
public interface DataRepository extends JpaRepository<FirstData, Long> {
DataService getById(Long id);
}
FirstData entity:
#Data
#Entity
#Table(schema = "someschema", name = "firstdata")
public class FirstData {
#Id
#Column(name = "id")
private Long uuid;
#Column(name = "name")
private String name;
#Column(name = "type")
private String type;
}
SecondData entity:
#Data
#Entity
#Table(schema = "someschema", name = "seconddata")
public class SecondData {
#Id
#Column(name = "id")
private Long uuid;
#Column(name = "type")
private String type;
#Column(name = "value")
private String value;
}
and DataService
#Service
public class DataService {
private DataRepository dataRepository;
public DataService(DataRepository dataRepository){
this.dataRepository = dataRepository;
}
public void getBothFirstAndSecondData() {
List<FirstData> firstDataSet = dataRepository.findAll();
}
}
I need to get data from both tables, but don't want to modify table structure, make jointable or add foreign keys. Also, i don't want to add another repository write code arount second data set. I need just to have a "value" from second data set at first data set result. What is the simpliest approach for solving such data retrieveing?
sql query the above problem can be:
select value from seconddata where id IN (select id from firstdata)

Enum type fields in JPA Entity

Is it possible to use Enums as a type of a field (column) in custom JPA entities? Here is an example:
#Getter #Setter
#Entity
#Table(name = "payments")
public class PaymentEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "status")
private Integer statusId;
public PaymentStatuses getStatus() {
return PaymentStatuses.valueOf(statusId);
}
public PaymentEntity setStatus(PaymentStatuses status) {
statusId = status == null ? null : status.getId();
return this;
}
}
public enum PaymentStatuses {
CREATED(1),
COMPLETED(2),
CANCELED(3);
private Integer id;
private PaymentStatuses(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public static PaymentStatuses valueOf(Integer id) {
for (PaymentStatuses value : values())
if (value.getId().equals(id))
return value;
return null;
}
}
Code above works fine, but approach with statusId and getStatus setStatus looks ugly a little bit.
I wanna use PaymentStatuses as a type of the field in my entity. Like this:
#Getter #Setter
#Entity
#Table(name = "payments")
public class PaymentEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "status")
private PaymentStatuses status;
}
Tell me please, is it possible?
Using #Enumerated(EnumType.ORDINAL) will not work because the ORDINAL mode starts to 0.
So the 3 first values of the enum will be represented in DB with the 0, 1 and 2 values.
while the id field in the enum to represent it in the DB goes from 1 to 3 :
CREATED(1),
COMPLETED(2),
CANCELED(3);
Besides, this way would correlate the order of elements defined in the enum with the way to represent them in database. Which not a good thing as enum values could be added/removed in the future.
A better way to address your issue is defining a javax.persistence.AttributeConverter and using it with #Convert.
So create a AttributeConverter implementation to indicate how to convert from the DB to the enum and the enum to the DB.
Then declare a PaymentStatuses status in your entity and annotate it with #Convert by specifying the AttributeConverter implementation class.
#Getter #Setter
#Entity
#Table(name = "payments")
public class PaymentEntity {
...
#Convert(converter = PaymentStatusesConverter.class)
private PaymentStatuses status;
...
}
public class PaymentStatusesConverter implements AttributeConverter<PaymentStatuses, Integer> {
#Override
public Integer convertToDatabaseColumn(PaymentStatuses status) {
return status.getId();
}
#Override
public PaymentStatuses convertToEntityAttribute(Integer status) {
return PaymentStatuses.valueOf(status);
}
}
Yes, but when you save to the database it will persist the current index of the enum value (in your case 0 for CREATED, 1 for COMPLETED, etc.) which will give you trouble if you change the enum values. To avoid this you can use the #Enumerated annotation like:
#Getter #Setter
#Entity
#Table(name = "payments")
public class PaymentEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "status")
#Enumerated(EnumType.ORDINAL) // There is also EnumType.STRING, or you can define a custom EnumType
private PaymentStatuses status;
}
You can use #Enumerated(EnumType.STRING) if you want your data to be stored in the db like the name of the Java enum name (CREATED, COMPLETED, CANCELED)

Categories

Resources