Custom query Parameter value [2] did not match expected type - spring

I trying to update a entity with OneToOne relation using restful web service.
I'm using a custom query but it doesnt work
#Modifying
#Query("UPDATE Activity a SET a.type = :type WHERE a.id = :uuid")
int setType(#Param("uuid") UUID uuid, #Param("type") long type);
Error :
java.lang.IllegalArgumentException: Parameter value [2] did not match expected type [com.mezoo.tdc.model.ActivityType (n/a)]
Activity Bean
#Entity
#Table(name= "Activity")
public class Activity implements Serializable{
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name="uuid", strategy = "uuid2")
#Column(name = "uuid", nullable = false, unique = true, columnDefinition = "BINARY(16)")
private UUID uuid;
#OneToOne(fetch = FetchType.EAGER, cascade={CascadeType.MERGE})
#JoinColumn(name="type", nullable = false)
private ActivityType type;
ActivityType Bean
#Entity
#Table(name= "ActivityType")
public class ActivityType implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(nullable = false, unique = true)
private String code;
#Column(nullable = false)
private String label;
It is possible to update without custom queries ?
I would like use POST request like below :
{"uuid":"9d9fa946-ee6e-408e-9e8a-7a9786a1d362","label":"ACTIVITY", "type":{"id":2}}
Update
Activity Repository :
#Transactional
public interface ActivityDao extends CrudRepository<Activity, UUID> {
#Modifying
#Query("UPDATE Activity a SET a.type = :type WHERE a.uuid = :uuid")
int updateType(#Param("uuid") UUID uuid, #Param("type") ActivityType type);
}
Activity Service:
#Service
public class ActivityService implements EntityService<Activity,UUID> {
private static final Logger LOGGER = LoggerFactory.getLogger(ActivityService.class);
#Override
public Activity update(Activity activity) {
LOGGER.info("Update ",activity);
Activity updated = activityDao.findOne(activity.getUuid());
if(updated==null) throw new ResourceNotFoundException();
if (activity.getType().getId()!= updated.getType().getId()) {
LOGGER.info("Update ActivityType for Activity "+activity.getUuid(),activity);
activityDao.updateType(updated.getUuid(),activity.getType());
}
updated.setLabel(activity.getLabel());
updated.setDetails(activity.getDetails());
return activityDao.save(updated);
}
Thanks

your parameter #Param("type") long type is wrong !
it should be #Param("type") ActivityType type.

Related

Spring JPA - How can I make JpaRepository queries using an #Embedded property?

I'm trying to make a existsBy query using a property that comes from an embedded class, but I'm receiving "No property 'cpf' found for type 'Patient'".
The class Patient uses the Person class as embedded.
Person.java
#Embeddable
#Data
public class Person {
#Column(nullable = false, length = 11)
private String cpf;
#Column(name = "full_name", nullable = false, length = 60)
private String fullName;
#Column(nullable = false)
private String birthdate;
#Column(name = "email", nullable = true, length = 30)
private String emailAddress;
#Column(name = "cellphone_number", nullable = true, length = 11)
private String cellphoneNumber;
}
Patient.java
#Data
#Entity
#Table(name = "tb_patient")
public class Patient implements Serializable {
#Serial
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "patient_id")
private UUID id;
#Column
private LocalDateTime registrationDate;
#Embedded
private Person Person;
}
PatientController.java (part of)
#PostMapping
public ResponseEntity<Object> savePatient(#RequestBody Person person) {
if(patientService.existsByCpf(person.getCpf())) {
return ResponseEntity.status(HttpStatus.CONFLICT).body("CONFLICT: CPF number is already in use!");
}
var patientModel = new Patient();
BeanUtils.copyProperties(person, patientModel);
patientModel.setRegistrationDate(LocalDateTime.now(ZoneId.of("UTC")));
return ResponseEntity.status(HttpStatus.CREATED).body(patientService.save(patientModel));
}
PatientService.java (part of)
#Service
public class PatientService {
final PatientRepository patientRepository;
public PatientService(PatientRepository patientRepository) {
this.patientRepository = patientRepository;
}
public boolean existsByCpf(String cpf) {
return patientRepository.existsByCpf((cpf));
}
PatientRepository.java
#Repository
public interface PatientRepository extends JpaRepository<Patient, UUID> {
boolean existsByCpf(String cpf);
}
How can I pass the #Embedded properties to the #Repository?
You can try separate by _ embedded filed name and it's filed.
#Repository
public interface PatientRepository extends JpaRepository<Patient, UUID> {
boolean existsByPerson_Cpf(String cpf);
}

JPA Failing to produce a proper SQL query when a parameter has a composite primary key

Today I came across a weird bug while trying to test a JPA update query and I'm wondering if this a SpringBoot bug.
I have the following entities
An Entry entity
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor(access = AccessLevel.PROTECTED, force = true)
public class Entry {
#Id
private String id;
#ManyToOne(targetEntity = User.class)
#JoinColumn(referencedColumnName = "username")
#NotNull
private final User username;
#Enumerated(EnumType.STRING)
#NotNull
private Type type;
#ManyToOne(targetEntity = Category.class)
#JoinColumns({#JoinColumn(referencedColumnName = "name"),#JoinColumn(referencedColumnName = "type"),#JoinColumn(referencedColumnName = "username")})
#NotNull
private Category category;
#Size(max = 45)
#NotBlank
private String description;
#NotNull
private Double amount;
#NotNull
private final Date createdAt;
private Timestamp lastUpdate;
#NotNull
private Boolean isDeleted;
public enum Type{
Income,Expense
}
}
A Category entity with a composite key
#Entity
#NoArgsConstructor(access = AccessLevel.PROTECTED, force = true)
#Setter
#Getter
#EqualsAndHashCode(of = {"id"})
#ToString(of = {"id"})
public class Category {
#EmbeddedId
private CategoryId id;
private final Timestamp createdAt = Timestamp.from(Instant.now());
#ManyToOne(targetEntity = User.class)
#JoinColumn(referencedColumnName = "username")
private final User user;
#OneToMany(cascade = CascadeType.ALL,mappedBy = "category")
private List<Entry> entries;
public Category(String name, Type type, User user){
this.id = new CategoryId(name,type,user.getUsername());
this.user = user;
}
}
A CategoryID that is the embeddable composite key of the Category entity
#Data
#Embeddable
#AllArgsConstructor
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#EqualsAndHashCode(of = {"name","type","username"})
public class CategoryId implements Serializable {
private String name;
#Enumerated(EnumType.STRING)
private Type type;
private String username;
}
The following repository
#Repository
public interface EntryRepository extends JpaRepository<Entry, String> {
Optional<Entry> findEntryById(String id);
#Modifying(clearAutomatically = true, flushAutomatically = true)
#Query(value = "UPDATE Entry e SET e.username = :username, e.type = :type, e.category = :category, e.description = :description, e.amount = :amount, e.createdAt = :date, e.lastUpdate = :lastUpdate, e.isDeleted = :isDeleted WHERE e.id = :id")
void update(#Param("id") String id,
#Param("username") User username,
#Param("type") Entry.Type type,
#Param("category") Category category,
#Param("description") String description,
#Param("amount") Double amount,
#Param("date") Date date,
#Param("lastUpdate") Timestamp lastUpdate,
#Param("isDeleted") Boolean isDeleted);
}
And finally the following Unit Test
#Test
void update() {
//given
User testUser = userRepository.save(new User("testUser#test.com","000000000000000000000000000000000000000000000000000000000000"));
Category testCategory = categoryRepository.save(new Category("Test Category", Entry.Type.Income,testUser));
Entry testEntry = new Entry("testEntry",testUser, Entry.Type.Income,
testCategory, "test",
0.0, new Date(343), from(now()), false);
System.out.println(testCategory);
entryRepositoryUnderTest.save(testEntry);
//when
entryRepositoryUnderTest.update("testEntry",testUser,Expense,testCategory,"testUpdated",1.0,new Date(346), from(now()),true);
Optional<Entry> actual = entryRepositoryUnderTest.findEntryById("testEntry");
System.out.println(actual.get().getCategory());
//then
assertThat(actual.get().getUsername()).isEqualTo(testUser);
assertThat(actual.get().getType()).isEqualTo(Expense);
assertThat(actual.get().getCategory()).isEqualTo(testCategory);
assertThat(actual.get().getDescription()).isEqualTo("testUpdated");
assertThat(actual.get().getAmount()).isEqualTo(1.0);
assertThat(actual.get().getIsDeleted()).isEqualTo(true);
}
When I run the test it fails and I get the following error message:
could not execute update query; SQL [update entry set username_username=?, type=?,category_name=?=category_type=?, description=?, amount=?, created_at=?, last_update=?, is_deleted=? where id=?]; nested exception is org.hibernate.exception.DataException: could not execute update query
As you can see here when SpringBoot is trying to produce a SQL query statement from my #Query parameter it can not properly extract the Category field from the parameters and inject it's composite embeddable key into the SQL statement. It has no problem extracting the User parameter because the User is an entity with an id that is not composite.
Is this a SpringBoot bug or am I missing something?
EDIT:
This is the structure of the database

Spring data jpa CRUDRepository/ JPArepository saveall can not get id (non primary key)

I am fetching data from FB marketing API and trying to save in DB. I am able to save data in the DB using CrudRepository or JpaRepository -> saveall method, but when trying to fetch the id in response of saveall, I am getting id as null. When I see in the h2-console, able to see the auto increment value after the completion of transaction.
Note: id is not used as primary key #Id. accountId is used as primary key.
Model:
#Entity
#Table(name = "accounts")
#Data
#ToString(onlyExplicitlyIncluded = true)
public class Account implements Serializable{
#JsonIgnore
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(columnDefinition = "integer auto_increment",insertable = false)
private Long id;
#JsonProperty("account_id")
#Column(name = "account_id")
#Id
private String accountId;
#Column(name = "account_status")
private int accountStatus;
#JsonProperty("timezone_id")
#Column(name = "timezone_id")
private int timezoneId;
private int timezoneOffsetUtc;
private String currency;
#Column(name = "timezone_name")
#JsonProperty("timezone_name")
private String timezoneName;
private String name;
#Column(name = "created_on",nullable = false, updatable = false)
#CreationTimestamp
private LocalDateTime createdOn;
#Column(name = "updated_on")
#UpdateTimestamp
private LocalDateTime updatedOn;
}
Repository:
#Repository()
public interface AccountRepository extends CrudRepository<Account, String> {
}
Tried with JpaRepository<Account, Long> too and flush after saving..but still getting id null in return list response of saveall()
Service:
#Service
public class AccountsService {
#Autowired
private AccountRepository repository;
#Override
#Transactional
public List<Account> saveAll(List<Account> accounts) {
//in case of JpaRepository
List<Account> savedAccounts= repository.saveAll(accounts);
repository.flush();
return savedAccounts;
//in case of CrudRepository
return (List<Account>)repository.saveAll(accounts);
}
}
when executing this
//accountsList received from FB API
List<Account> savedList=iAccountsService.saveAll(accountsList);
savedList.get(0).getId() **//this is coming as null**
Any sort of help is appreciated.
In your entity class :
Use this #GeneratedValue(strategy = GenerationType.IDENTITY)
public class Account implements Serializable{
#JsonIgnore
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false, insertable = false, updatable = false)
private Long id;
}

UUID Mapping in hibernate

I have mapped a table to my table and trying to add some values in it. but I am getting errors as below
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'create, delete, read, role_id, update, id) values
(_binary'ØN_WlAs—\niÊnÙ' at line 1
my entities are
RoleSettings.java
#Entity #Table(name = "role_settings")
#Getter #Setter #Data
public class RoleSettings implements Serializable {
private static final long serialVersionUID = 8862104773442047690L;
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#ManyToOne
#JoinColumn(name = "role_id", referencedColumnName = "id", foreignKey = #ForeignKey(name = "role_settings_iam_role_FK"))
private RoleMaster roleId;
}
RoleMaster.java
#Entity #Table(name = "role")
#Getter #Setter #Data
public class RoleMaster implements Serializable {
private static final long serialVersionUID = 1792968151371176640L;
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#Column(name = "name", nullable = false, length = 255)
private String name;
}
RoleSettingsRepository.java
public interface RoleSettingsRepository extends JpaRepository<RoleSettings, UUID>{}
RoleSettingsService.java
#Service
Class RoleSettingsService {
#Autowired
private RoleSettingsRepository roleSettingsRepository;
public BaseDTO create(RoleSettings roleSettings) {
BaseDTO response = new BaseDTO();
RoleSettings newRoleSettings = new RoleSettings();
try {
newRoleSettings.setRoleId(roleSettings.getRoleId());
newRoleSettings.setAppAccessId(roleSettings.getAppAccessId());
newRoleSettings.setCreate(roleSettings.getCreate());
newRoleSettings.setUpdate(roleSettings.getUpdate());
newRoleSettings.setRead(roleSettings.getRead());
newRoleSettings.setDelete(roleSettings.getDelete());
roleSettingsRepository.save(newRoleSettings);
response.setStatusCode(200);
} catch (Exception e) {
}
return response;
}
}
RoleSettingsController.java
#RestController
#RequestMapping("/v1/rolesettings")
public class RoleSettingsController {
#Autowired
private RoleSettingsService roleSettingsService;
#PostMapping("/post")
public BaseDTO create(#RequestBody RoleSettings roleSettings) {
BaseDTO response = roleSettingsService.create(roleSettings);
return response;
}
}
my json object
{ "roleId" :{"id": "b2e64c82-ab75-41d3-bb10-e9150f314807"} }
and my roleId is stored in database as type binary(16).
Check in your database data type of the id column. It has to be BINARY(16). And annotate your entity field as:
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
#Column(columnDefinition = "BINARY(16)")
private UUID id;
Note that you nned to add a column definition in this case.

Add entity with OneToOne Relation using JPA and REST

I am using Spring JPA Restful, and I don't understand how to insert an entity with a foreign key.
Activity Entity:
#Entity
#Table(name= "Activity")
public class Activity implements Serializable{
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name="uuid", strategy = "uuid2")
#Column(name = "uuid", nullable = false, unique = true)
private UUID uuid;
#OneToOne(fetch = FetchType.EAGER, cascade=CascadeType.MERGE)
#JoinColumn(name="type", nullable = false)
private ActivityType type;
#Column(nullable = false)
private String label;
ActivityType Entity:
#Entity
#Table(name= "ActivityType")
public class ActivityType implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(nullable = false, unique = true)
private String code;
#Column(nullable = false
private String label;
Is it possible to insert Activity simply? With something like this JSON where ActivityType's id "1" exists:
createActivity:
{"label":"LABEL","type":1}
With this code I have to do:
createActivity:
{"label":"LABEL","type":{"id":1}}
which return value is:
{
"uuid": "a54b27aa-8d49-41fd-8976-70c019c40e3b",
"type": {
"id": 1,
"code": null,
"label": null
},
"label": "LABEL",
"details": null
}
I use the library gson for parsing domain classes into JSON.
//... code for making your activity, let's say you have an Activity object myActivity
Just add the following code where you want to parse your object into JSON.
Gson gson = new GSON();
String json = gson.toJson(myActivity);

Resources