山一程--软件开发--Spring jpa2 hibernate
目的:基于<spring 5 高级编程> chapter 7 8, 精研 JPA hibernate, ORM, 实体关系映射,事务管理.
2023-04-19
1. mysql 8.0.2

create user 'PRACTICE_SPRING_JPA_HIBERNATE_P2'@'LOCALHOST' IDENTIFIED BY 'admin';
create schema PRACTICE_SPRING_JPA_HIBERNATE_P2 character set 'utf8mb4';
grant all privileges on PRACTICE_SPRING_JPA_HIBERNATE_P2 .* TO 'PRACTICE_SPRING_JPA_HIBERNATE_P2'@'LOCALHOST';
flush privileges;
SET GLOBAL time_zone = "+3:00";
CREATE TABLE SINGER(
ID bigint NOT NULL primary key auto_increment,
FIRST_NAME VARCHAR(60) NOT NULL,
LAST_NAME VARCHAR(60) NOT NULL,
BIRTH_DATE DATE,
VERSION INT NOT NULL DEFAULT 0,
UNIQUE UQ_SINGER_1 ( FIRST_NAME, LAST_NAME )
) ENGINE = InnoDB;
CREATE TABLE ALBUM(
ID BIGINT NOT NULL PRIMARY KEY auto_increment,
SINGER_ID BIGINT NOT NULL,
TITLE VARCHAR(100) NOT NULL,
RELEASE_DATE DATE,
VERSION INT NOT NULL DEFAULT 0,
UNIQUE UQ_SINGER_ALBUM_1 (SINGER_ID, TITLE ),
CONSTRAINT FK_ALBUM_SINGER foreign key (SINGER_ID) REFERENCES SINGER(ID)
)ENGINE = InnoDB;
CREATE TABLE INSTRUMENT(
INSTRUMENT_ID VARCHAR(20) NOT NULL PRIMARY KEY
)engine = InnoDB;
CREATE TABLE SINGER_INSTRUMENT(
SINGER_ID BIGINT NOT NULL,
INSTRUMENT_ID VARCHAR(20) NOT NULL,
PRIMARY KEY(SINGER_ID, INSTRUMENT_ID),
CONSTRAINT FK_SINGER_INSTRUMENT_1 FOREIGN KEY (SINGER_ID) REFERENCES SINGER(ID) ON DELETE CASCADE,
CONSTRAINT FK_SINGER_INSTRUCMENT_2 foreign key (INSTRUMENT_ID) references INSTRUMENT(INSTRUMENT_ID)
)ENGINE = InnoDB;
2023-04-20




@Configuration
@ComponentScan(
basePackages = {
"org.tech.road.springjpahibernatepractice2.domain.model",
"org.tech.road.springjpahibernatepractice2.port.adapter.persistence"
})
@EnableTransactionManagement
public class JpaConfig {
private static Logger logger = LoggerFactory.getLogger(JpaConfig.class);
@Bean
public Properties hibernateProperties() {
Properties hibernateProps = new Properties();
hibernateProps.put("hibernate.dialect",
"org.hibernate.dialect.MySQL8InnoDBDialect");
hibernateProps.put("hibernate.max_fetch_depth", 3);
hibernateProps.put("hibernate.jdbc.batch_size", 10);
hibernateProps.put("hibernate.jdbc.fetch_size", 50);
hibernateProps.put("hibernate.format_sql", true);
hibernateProps.put("hibernate.use_sql_comments", true);
hibernateProps.put("hibernate.show_sql", true);
return hibernateProps;
}
@Bean
public SessionFactory sessionFactory(DataSource dataSource)
throws IOException {
LocalSessionFactoryBean sessionFactoryBean =
new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
sessionFactoryBean.setPackagesToScan(new String[]{
"org.tech.road.springjpahibernatepractice2.domain.model"
});
sessionFactoryBean.setHibernateProperties(hibernateProperties());
sessionFactoryBean.afterPropertiesSet();
return sessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager(SessionFactory sessionFactory)
throws IOException{
return new HibernateTransactionManager(sessionFactory);
}
}
java entity
@Entity @Table(name="singer") public class Singer implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ID") private Long id; @Version @Column(name = "VERSION") private int version; @Temporal(TemporalType.DATE) @Column(name="BIRTH_DATE") private Date birthDate; @Column(name="FIRST_NAME") private String firstName; @Column(name="LAST_NAME") private String lastName; @OneToMany( mappedBy = "singer", cascade = CascadeType.ALL, orphanRemoval = true) private Set<Album> albums = new HashSet<>(); @ManyToMany @JoinTable( name="singer_instrument", joinColumns = @JoinColumn(name="SINGER_ID"), inverseJoinColumns = @JoinColumn(name="INSTRUMENT_ID")) private Set<Instrument> instruments = new HashSet<>(); public Long getId() { return id; } public int getVersion() { return version; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Date getBirthDate() { return birthDate; } public void setBirthDate(Date birthDate) { this.birthDate = birthDate; } public Set<Album> getAlbums() { return albums; } public void setAlbums(Set<Album> albums) { this.albums = albums; } public Set<Instrument> getInstruments() { return instruments; } public void setInstruments(Set<Instrument> instruments) { this.instruments = instruments; } @Override public String toString() { return "Singer{" + "id=" + id + ", version=" + version + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", birthDate=" + birthDate + '}'; }
@Entity @Table(name="album") public class Album implements Serializable { @ManyToOne @JoinColumn(name="SINGER_ID") private Singer singer; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="ID") private Long id; @Version @Column(name="VERSION") private int Version; @Column(name="TITLE") private String title; @Temporal(TemporalType.DATE) @Column(name="RELEASE_DATE") private Date releaseDate; public Long getId() { return id; } public int getVersion() { return Version; } public void setVersion(int version) { Version = version; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Date getReleaseDate() { return releaseDate; } public void setReleaseDate(Date releaseDate) { this.releaseDate = releaseDate; } public Singer getSinger() { return singer; } public void setSinger(Singer singer) { this.singer = singer; } @Override public String toString() { return "Album{" + "singer=" + singer + ", id=" + id + ", Version=" + Version + ", title='" + title + '\'' + ", releaseDate=" + releaseDate + '}'; } }
@Entity @Table(name="instrument") public class Instrument implements Serializable { @ManyToMany @JoinTable(name="singer_instrument", joinColumns = @JoinColumn(name="INSTRUMENT_ID"), inverseJoinColumns = @JoinColumn(name="SINGER_ID")) private Set<Singer> singers = new HashSet<>(); @Id @Column(name="INSTRUMENT_ID") private String instrumentId; public String getInstrumentId() { return instrumentId; } public void setInstrumentId(String instrumentId) { this.instrumentId = instrumentId; } public Set<Singer> getSingers() { return singers; } public void setSingers(Set<Singer> singers) { this.singers = singers; } @Override public String toString() { return "Instrument{" + "instrumentId='" + instrumentId + '\'' + '}'; } }
insert
Singer Set<Album>
插入单个 Album 时,使用 Album.setSinger(this) 方法关联起来,
Album 不做任何动作,Singer 处理.




暂时记录:
一对多时,在1 对象里 add (多的对象) ,集合要条件, 而多对象的类里不用操作.
不在双方都使用 @JoinColum, 一是少了维护一张关系映射表, 无须双方都维护外键,只由“多方” 维护即可.


多对多时,示例里未做操作。保存出现异常:




--------------------------------------------------------------------------------------
多对多


报未持久化instrument 时
解决:
1.原因在于 Instrument 主键是 char, 非自增,需要手动设置该表主键。

注意其他错误:@ManyToMany 或类似注解中, mappedBy 不能与 @JointTable @JoinColumn 一起使用。冲突



@Entity @Table(name="singer") @NamedQueries({ @NamedQuery(name="Singer.findById", query = "select distinct s from Singer s " + "left join fetch s.albums a " + "left join fetch s.instruments i " + "where s.id = :id"), @NamedQuery(name="Singer.findAllWithAlbum", query="select distinct s from Singer s "+ "left join fetch s.albums a " + "left join fetch s.instruments i") }) public class Singer implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ID") private Long id; @Version @Column(name = "VERSION") private int version; @Temporal(TemporalType.DATE) @Column(name="BIRTH_DATE") private Date birthDate; @Column(name="FIRST_NAME") private String firstName; @Column(name="LAST_NAME") private String lastName; @OneToMany( mappedBy = "singer", cascade = CascadeType.ALL, orphanRemoval = true) private Set<Album> albums = new HashSet<>(); public Set<Album> getAlbums() { return albums; } public void setAlbums(Set<Album> albums) { this.albums = albums; } public boolean addAlbum(Album album){ album.setSinger(this); return getAlbums().add(album); } public void removeAlbum(Album album){ getAlbums().remove(album); } @ManyToMany @JoinTable( name="singer_instrument", joinColumns = @JoinColumn(name="SINGER_ID"), inverseJoinColumns = @JoinColumn(name="INSTRUMENT_ID")) private Set<Instrument> instruments = new HashSet<>(); public Set<Instrument> getInstruments() { return instruments; } public void setInstruments(Set<Instrument> instruments) { this.instruments = instruments; } public boolean addInstrument(Instrument instrument){ return getInstruments().add(instrument); } public Long getId() { return id; } public int getVersion() { return version; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Date getBirthDate() { return birthDate; } public void setBirthDate(Date birthDate) { this.birthDate = birthDate; } @Override public String toString() { return "Singer{" + "id=" + id + ", version=" + version + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", birthDate=" + birthDate + '}'; } }
@Entity @Table(name="album") public class Album implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="ID") private Long id; @Version @Column(name="VERSION") private int Version; @Column(name="TITLE") private String title; @Temporal(TemporalType.DATE) @Column(name="RELEASE_DATE") private Date releaseDate; @ManyToOne @JoinColumn(name="SINGER_ID") private Singer singer; public Singer getSinger() { return singer; } public void setSinger(Singer singer) { this.singer = singer; } public Long getId() { return id; } public int getVersion() { return Version; } public void setVersion(int version) { Version = version; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Date getReleaseDate() { return releaseDate; } public void setReleaseDate(Date releaseDate) { this.releaseDate = releaseDate; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Album)) return false; Album album = (Album) o; return Version == album.Version && Objects.equals(id, album.id) && Objects.equals(title, album.title) && Objects.equals(releaseDate, album.releaseDate); } @Override public int hashCode() { return Objects.hash(id, Version, title, releaseDate); } @Override public String toString() { return "Album{" + "singer=" + singer + ", id=" + id + ", Version=" + Version + ", title='" + title + '\'' + ", releaseDate=" + releaseDate + '}'; } }
@Entity @Table(name="instrument") public class Instrument implements Serializable { @Id @Column(name="INSTRUMENT_ID") private String instrumentId; @ManyToMany @JoinTable(name="singer_instrument", joinColumns = @JoinColumn(name="INSTRUMENT_ID"), inverseJoinColumns = @JoinColumn(name="SINGER_ID")) private Set<Singer> singers = new HashSet<>(); public String getInstrumentId() { return instrumentId; } public void setInstrumentId(String instrumentId) { this.instrumentId = instrumentId; } public Set<Singer> getSingers() { return singers; } public void setSingers(Set<Singer> singers) { this.singers = singers; } @Override public String toString() { return "Instrument{" + "instrumentId='" + instrumentId + '\'' + '}'; } }
@SpringBootApplication public class SpringJpaHibernatePractice2Application { public static void main(String[] args) { SpringApplication.run(SpringJpaHibernatePractice2Application.class, args); } @Component class App implements ApplicationRunner{ @Autowired SingerDao singerDao; @Override public void run(ApplicationArguments args) throws Exception { //insertSingerWithAlbumAndInstruments(); insertInstruments(); //updateAndDeleteOrphan(); /* List<Singer> findAll = findAll(); listAllInfo(findAll);*/ //deleteCascade(); } // insert private void insertSingerWithAlbumAndInstruments(){ Singer singer = new Singer(); singer.setFirstName("bb11"); singer.setLastName("King se"); singer.setBirthDate(new Date( new GregorianCalendar(1940, 8, 16) .getTime().getTime())); Album album = new Album(); album.setTitle("My Kind of Blues"); album.setReleaseDate(new Date(new GregorianCalendar(1934, 4, 2).getTime().getTime())); singer.addAlbum(album); album = new Album(); album.setTitle("A Heart Full of Blues"); album.setReleaseDate(new Date(new GregorianCalendar(1924,5,6) .getTime().getTime())); singer.addAlbum(album); singerDao.save(singer); } private void insertInstruments(){ Singer singer = new Singer(); singer.setFirstName("cc11"); singer.setLastName("Eric ex"); singer.setBirthDate(new Date(new GregorianCalendar(1924, 5, 3).getTime().getTime())); Instrument instrument = new Instrument(); instrument.setInstrumentId("Guitar"); singer.addInstrument(instrument); Instrument instrumentAnother = new Instrument(); instrumentAnother.setInstrumentId("Piano"); singer.addInstrument(instrumentAnother); singerDao.save(singer); } private void updateAndDeleteOrphan(){ Singer singer = singerDao.findById(4L); Album album = singer.getAlbums().stream() .filter(e->e.getTitle() .equalsIgnoreCase("My Kind of Blues")) .findFirst() .get(); singer.setFirstName("aaa"); singer.removeAlbum(album); singerDao.save(singer); } private List<Singer> builderSingers(){ Singer singer1 = new Singer(); singer1.setFirstName("Eric"); singer1.setLastName("Clapton"); singer1.setBirthDate(new Date()); return List.of(singer1); } private List<Singer> findAll(){ return this.singerDao.findAllWithAlbum(); } private void listAllInfo(List<Singer> singers){ singers.stream().forEachOrdered(s->{ if(s.getAlbums().size() > 0){ s.getAlbums().stream().forEachOrdered(a->{ System.out.println( String.format("s: {} ,id: {},title:{}",s.getId(), a.getId(),a.getTitle())); }); } if(s.getInstruments().size()>0){ s.getInstruments().stream().forEachOrdered(i->{ System.out.println( String.format("s:{}, id: {}",s.getId(),i.getInstrumentId()) ); }); } }); } private void deleteCascade(){ Singer singer = singerDao.findById(9L); singerDao.delete(singer); } } }
查询: NamedQuery HQL 注意 distinct 关键字。




find list

删除:



实际数据库保存了对象的状态,而对象的状态由其字段的值定义的,而不是访问器返回的值.精确地按照数据库的方式重新创建对象.
注解字段允许在 setter 执行额外的处理,从数据库中加载数据后对其进行精密计算,而属性访问的问题在于,在加载对象时也会调用 setter 方法.
Hibernate 如何管理会话的内部机制很重要,特别时批处理中,Hibernate 将在会话中托管被管理对象,并定期刷新和清除它们。
设计不佳的数据访问逻辑可能会导致 Hibernate 频繁刷新会话并极大影响性能.
要在会话工厂中定义它们, 批处理大小,获取大小在调整Hibernate 性能方面很重要.

浙公网安备 33010602011771号