Reputation: 12005
I am developing Spring Boot JPA Composite key example using Postgres. In this example, when I'm trying to save record, why I dont see any exceptions or constraint violation exception?
SongId.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Embeddable
public class SongId implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String album;
private String artist;
}
Song.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Song {
@EmbeddedId
private SongId id;
private int duration;
private String genre;
private LocalDateTime releaseDate;
private int rating;
private String downloadUrl;
}
SongsRepository.java
public interface SongsRepository extends JpaRepository<Song, Long>{
}
MainApp.java
@SpringBootApplication
public class CompositeApplication implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(CompositeApplication.class, args);
}
@Autowired
private SongsRepository repo;
@Override
public void run(String... args) throws Exception {
SongId songId1 = SongId.builder().name("John").album("AlbumA").artist("ArtistA").build();
Song song = Song.builder().id(songId1).downloadUrl("http://www.gmail.com").duration(23)
.genre("MyGene").rating(1).releaseDate(LocalDateTime.now()).build();
Song song2 = Song.builder().id(songId1).downloadUrl("http://www.gmail.com").duration(23)
.genre("MyGene").rating(1).releaseDate(LocalDateTime.now()).build();
try {
repo.saveAll(Arrays.asList(song, song2));
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
Edit-1: I changed SongsRepository.java to
public interface SongsRepository extends CrudRepository<Song, SongId>{}
and main Method code.
@Override
public void run(String... args) throws Exception {
SongId songId1 = SongId.builder().name("John").album("AlbumA").artist("ArtistA").build();
Song song = Song.builder().songId(songId1).downloadUrl("http://www.gmail.com").duration(23)
.genre("MyGene").rating(4).releaseDate(LocalDateTime.now()).build();
Song song2 = Song.builder().songId(songId1).downloadUrl("http://www.yahoo.com").duration(25)
.genre("Sample Testung").rating(2).releaseDate(LocalDateTime.now()).build();
repo.saveAll(Arrays.asList(song, song2));
}
Logs:
2019-07-03 19:56:31.901 INFO 5420 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 46ms. Found 1 repository interfaces.
2019-07-03 19:56:32.265 INFO 5420 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-07-03 19:56:32.378 INFO 5420 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2019-07-03 19:56:32.415 INFO 5420 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [
name: default
...]
2019-07-03 19:56:32.466 INFO 5420 --- [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.3.10.Final}
2019-07-03 19:56:32.467 INFO 5420 --- [ main] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found
2019-07-03 19:56:32.600 INFO 5420 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.0.4.Final}
2019-07-03 19:56:32.747 INFO 5420 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
2019-07-03 19:56:32.895 INFO 5420 --- [ main] o.h.e.j.e.i.LobCreatorBuilderImpl : HHH000421: Disabling contextual LOB creation as hibernate.jdbc.lob.non_contextual_creation is true
2019-07-03 19:56:32.899 INFO 5420 --- [ main] org.hibernate.type.BasicTypeRegistry : HHH000270: Type registration [java.util.UUID] overrides previous : org.hibernate.type.UUIDBinaryType@4ad3d266
Hibernate:
drop table if exists composite.song cascade
Hibernate:
create table composite.song (
album varchar(255) not null,
artist varchar(255) not null,
name varchar(255) not null,
download_url varchar(255),
duration int4 not null,
genre varchar(255),
rating int4 not null,
release_date timestamp,
primary key (album, artist, name)
)
2019-07-03 19:56:33.350 INFO 5420 --- [ main] o.h.t.schema.internal.SchemaCreatorImpl : HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@41ad373'
2019-07-03 19:56:33.352 INFO 5420 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2019-07-03 19:56:33.404 DEBUG 5420 --- [ main] .c.JpaMetamodelMappingContextFactoryBean : Initializing JpaMetamodelMappingContext…
2019-07-03 19:56:33.410 DEBUG 5420 --- [ main] .c.JpaMetamodelMappingContextFactoryBean : Finished initializing JpaMetamodelMappingContext!
2019-07-03 19:56:33.557 DEBUG 5420 --- [ main] o.s.d.r.c.s.RepositoryFactorySupport : Initializing repository instance for com.example.repository.SongsRepository…
2019-07-03 19:56:33.603 DEBUG 5420 --- [ main] o.s.d.j.r.query.JpaQueryFactory : Looking up query for method findBySongId_AlbumAndSongId_ArtistAndSongId_Name
2019-07-03 19:56:33.604 DEBUG 5420 --- [ main] o.s.d.jpa.repository.query.NamedQuery : Looking up named query Song.findBySongId_AlbumAndSongId_ArtistAndSongId_Name
2019-07-03 19:56:33.606 DEBUG 5420 --- [ main] o.s.d.jpa.repository.query.NamedQuery : Did not find named query Song.findBySongId_AlbumAndSongId_ArtistAndSongId_Name
2019-07-03 19:56:33.646 DEBUG 5420 --- [ main] o.s.d.r.c.s.RepositoryFactorySupport : Finished creation of repository instance for com.example.repository.SongsRepository.
2019-07-03 19:56:33.713 INFO 5420 --- [ main] com.example.CompositeApplication : Started CompositeApplication in 2.59 seconds (JVM running for 3.333)
2019-07-03 19:56:33.722 DEBUG 5420 --- [ main] stomAnnotationTransactionAttributeSource : Adding transactional method 'saveAll' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Hibernate:
select
song0_.album as album1_0_0_,
song0_.artist as artist2_0_0_,
song0_.name as name3_0_0_,
song0_.download_url as download4_0_0_,
song0_.duration as duration5_0_0_,
song0_.genre as genre6_0_0_,
song0_.rating as rating7_0_0_,
song0_.release_date as release_8_0_0_
from
composite.song song0_
where
song0_.album=?
and song0_.artist=?
and song0_.name=?
Hibernate:
insert
into
composite.song
(download_url, duration, genre, rating, release_date, album, artist, name)
values
(?, ?, ?, ?, ?, ?, ?, ?)
Hibernate:
update
composite.song
set
download_url=?,
duration=?,
genre=?,
rating=?,
release_date=?
where
album=?
and artist=?
and name=?
2019-07-03 19:56:33.773 INFO 5420 --- [ Thread-4] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2019-07-03 19:56:33.776 INFO 5420 --- [ Thread-4] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2019-07-03 19:56:33.779 INFO 5420 --- [ Thread-4] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Upvotes: 1
Views: 8093
Reputation: 228
Spring Data Jpa Repository functionality is implemented via the SimpleJpaRepository class containing following save(..) method:
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
Thus the Spring Jpa Data Repository save(...) method merges an already existing entity.
Opposed to that the naked EntityManager#persist() throws an exception if invoked with already existing entity.
The problem might be solved by adding custom behavior to Spring Data Repository/ies.
Alternatively : you can follow this
The easiest (and least invasive) way to work around this is probably by making sure the id only gets set right before the persist. This can be achieved in a @PrePersist callback:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Song {
@EmbeddedId
private SongId id;
private int duration;
private String genre;
private LocalDateTime releaseDate;
private int rating;
private String downloadUrl;
@PrePersist
void initIdentifier() {
SongId songId1 = SongId.builder().name("John").album("AlbumA").artist("ArtistA").build();
if (id == null) {
this.id = … songId1 // Create ID instance here.
}
}
}
Upvotes: 0
Reputation: 668
Change:
public interface SongsRepository extends JpaRepository<Song, Long>
to
public interface SongsRepository extends JpaRepository<Song, SongId>
Upvotes: 2