山一程--软件开发--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);
  }
}
config

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 +
                '}';
    }
Singer
@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 +
                '}';
    }
}
Album
@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 + '\'' +
                '}';
    }
}
Instrument

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 +
                '}';
    }
}
Singer
@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 +
                '}';
    }
}
Album
@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 + '\'' +
                '}';
    }
}
Instrument
@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);
    }
  }

}
Application

  查询: NamedQuery HQL 注意 distinct 关键字。

find list

 

删除:

 


 实际数据库保存了对象的状态,而对象的状态由其字段的值定义的,而不是访问器返回的值.精确地按照数据库的方式重新创建对象.

注解字段允许在 setter 执行额外的处理,从数据库中加载数据后对其进行精密计算,而属性访问的问题在于,在加载对象时也会调用 setter 方法.

Hibernate 如何管理会话的内部机制很重要,特别时批处理中,Hibernate 将在会话中托管被管理对象,并定期刷新和清除它们。

设计不佳的数据访问逻辑可能会导致 Hibernate 频繁刷新会话并极大影响性能.

要在会话工厂中定义它们, 批处理大小,获取大小在调整Hibernate 性能方面很重要.

posted @ 2023-04-19 21:06  君子之行  阅读(15)  评论(0)    收藏  举报