I have this Entity class with nickname String as a key.
@Data
@Entity
@Table(name="players")
public class Player {
@Id
private String nickname;
@Column(name="name")
private String name;
@Column(name="surname")
private String surname;
...
...
...
}
Controller
@PostMapping(value="/addPlayer", consumes = {MediaType.APPLICATION_JSON_VALUE}, produces ={MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Player> save(@RequestBody Player player) throws Exception {
return new ResponseEntity<>(playerService.save(player), HttpStatus.OK);
}
Service (interface)
Player save(Player player) throws Exception;
Service Impl
@Override
public Player save(Player player)throws Exception{
if(player.getName() != null && (!player.getName().isEmpty() && (!player.getName().isBlank())) &&
(player.getSurname() != null && (!player.getSurname().isEmpty() && (!player.getSurname().isBlank()))) &&
(player.getNickname() != null && (!player.getNickname().isEmpty() && (!player.getNickname().isBlank())))) {
return playerRepository.save(player);
}
throw new IllegalArgumentException("provide name,surname and nickname");
}
Repository
@Repository
public interface PlayerRepository extends JpaRepository<Player, String> {
//somestuff
}
I have this situation in DB.
If I try this request :
then in DB i have this bad behavior
Data are overwrite on key " nickname5
"
work like a patch :( , I don't understand how and why.
I try to add unique annotation at nickname column but not work ...
@Id
@Column(unique = true)
private String nickname;
Can I ask why i have this behavior and how I can fix it ?
(yes I can do manually with some check in a service but I think I miss some annotation or functionality about data-jpa-framework)
Other than this bug, if you see things that are not right or things that can be done better in the code, I welcome suggestions. I am here to improve . Thank you !
@Edit
I have add the unique constraint on 'nickname' pk - uk But behavior is the same. Name and Username are update if I pass a Nickname(primary-key) already in the database
As we discussed into comments this is my answer and some explanation about your case:
First of all you have to avoid using String
as primary key because in this case because when you are going to add new use with return playerRepository.save(player);
and a row with the same value as primary key is already persisted to database it will do update instead to throw exception, because save()
method from JpaRepository
work so.
JPA follows the latter approach. save() in Spring Data JPA is backed by merge() in plain JPA, therefore it makes your entity managed as described above. It means that calling save() on an object with predefined id will update the corresponding database record rather than insert a new one, and also explains why save() is not called create().
In your case you should have something like this one approach:
Entity:
@Data
@Entity
@Table(name="players")
public class Player {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "nickname", unique = true)
private String nickname;
@Column(name="name")
private String name;
@Column(name="surname")
private String surname;
}
Database Table:
CREATE TABLE IF NOT EXISTS players
(
id bigserial
primary key,
name varchar(255),
nickname varchar(255)
unique,
surname varchar(255)
);
And when you will try to persist a value that already exist in database with the same nickname
:
You will have the next one exception:
ERROR: duplicate key value violates unique constraint "players_nickname_key" Detail: Key (nickname)=(nickname) already exists.