ORM

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ณต์žกํ•ด์ง์— ๋”ฐ๋ผ, ํผ์‹œ์Šคํ„ด์Šค์™€ ๊ด€๋ จ๋œ ์š”๊ตฌ์‚ฌํ•ญ๋„ ๋ณต์žกํ•ด์ง ==> ๋” ๋ณต์žกํ•˜๊ณ  ์ •๊ตํ•œ ๊ธฐ๋Šฅ์ด ํ•„์š”

* ์ง€์—ฐ ๋กœ๋”ฉ(lazy loading) : ๊ด€๋ จ์„ฑ ์žˆ๋Š” ๊ฐ์ฒด ์ „์ฒด๋ฅผ ํ•œ๋ฒˆ์— ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ์ง€ ์•Š์„ ๋•Œ, ํ•„์š”๋กœ ํ•˜๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ๋งŒ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ธฐ๋Šฅ. ex) ๊ฐ์ฒด ์•ˆ์˜ ๋‚ด๋ถ€๊ฐ์ฒด๋Š” ์ œ์™ธํ•˜๊ณ  ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

* ์กฐ๊ธฐ ์ธ์ถœ(eager fetching) : ์ง€์—ฐ ๋กœ๋”ฉ์˜ ๋ฐ˜๋Œ€ ๊ฐœ๋…. ํ•œ๋ฒˆ์˜ ์ฟผ๋ฆฌ๋กœ ์ „์ฒด ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ธฐ๋Šฅ // ex) ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ํ•œ๋ฒˆ์œผ๋กœ, ๊ฐ์ฒด์™€ ๋‚ด๋ถ€๊ฐ์ฒด์˜ ์ •๋ณด๋ฅผ ๋ชจ๋‘ ๊ฐ€์ ธ์˜ด

* ์บ์Šค์ผ€์ด๋”ฉ(cascading) : ๋•Œ๋•Œ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ๋ณ€๊ฒฝํ–ˆ์„ ๋•Œ, ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”์˜ ๊ฐ’๋„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•˜๋Š” ๊ธฐ๋Šฅ // ex) ๊ฐ์ฒดA๋ฅผ ์‚ญ์ œํ•  ๋•Œ, ์—ฐ๊ด€๋œ B๊ฐ์ฒด๋„ ๊ฐ™์ด ์‚ญ์ œ

=> ์ด๋Ÿฌํ•œ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ํ”„๋ ˆ์ž„ ์›Œํฌ๋ฅผ '๊ฐ์ฒด ๊ด€๊ณ„ ๋งคํ•‘(ORM, Object-Relational Mapping, ์ดํ›„ ORM)'์ด๋ผ๊ณ  ํ•จ.

=> ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํผ์‹œ์Šคํ„ด์Šค ๊ณ„์ธต์— ORM์ด๋ผ๊ณ  ํ•จ. ์ข…๋ฅ˜๋กœ๋Š” ํ•˜์ด๋ฒ„๋„ค์ดํŠธ, iBATIS, myBATIS, JPA๋“ฑ์ด ํฌํ•จ๋œ๋‹ค.

* ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ : ์„ ์–ธ์  ํŠธ๋žœ์žญ์…˜์— ๋Œ€ํ•œ ํ†ตํ•ฉ ์ง€์›, ํˆฌ๋ช…ํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ, ์Šค๋ ˆ๋“œ ์•ˆ์ •์„ฑ์„ ๊ฐ–์ถ˜ ๊ฒฝ๋Ÿ‰์˜ ํ…œํ”Œ๋ฆฟ, DAO ์ง€์›, ์ž์› ๊ด€๋ฆฌ


์Šคํ”„๋ง๊ณผ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ํ†ตํ•ฉ

ํ•˜์ด๋ฒ„๋„ค์ดํŠธ : ์˜คํ”ˆ์†Œ์Šค ํผ์‹œ์Šคํ„ด์Šค ํ”„๋ ˆ์ž„์›Œํฌ, ์ œ๋Œ€๋กœ ๋œ ORM์ด ๊ฐ–์ถฐ์•ผํ•  ๊ธฐ๋Šฅ์„ ๋ชจ๋‘ ์ œ๊ณต(์บ์‹œ, ์ง€์—ฐ ๋กœ๋”ฉ, ์กฐ๊ธฐ ์ธ์ถœ, ๋ถ„์‚ฐ ์บ์‹œ ๋“ฑ)


ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์„ธ์…˜ ํŒฉํ† ๋ฆฌ ์„ ์–ธ

org..hibernate.Session : ๊ฐ€์žฅ ์ค‘์‹ฌ์ธ ์ธํ„ฐํŽ˜์ด์Šค,, ๊ฐ์ฒด๋ฅผ ์ €์žฅ, ์—…๋ฐ์ดํŠธ, ์‚ญ์ œ, ๋กœ๋“œํ•˜๋Š” ๊ธฐ๋ณธ์ ์ธ ์•ก์„ธ์Šค ๊ธฐ๋Šฅ์„ ์ œ๊ณต

์Šคํ”„๋ง์—์„œ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ SessionFactory๋ฅผ ์–ป๋Š” ๋ฐฉ๋ฒ•์€, ์„ธ์…˜ํŒฉํ† ๋ฆฌ ๋นˆ์„ ํ†ตํ•จ -> org.springframework.orm.hibernate4.LocalSessionFactoryBean (ํ•˜์ด๋ฒ„๋„ค์ดํŠธ3์€ ์ƒ๋žต)

ํ•˜์ด๋ฒ„๋„ค์ดํŠธ4๋ฅผ ๊ธฐ์ค€์œผ๋กœ, 3์˜ AnnotationSesstionFactoryBean์˜ ๋งค์‹œ์—…๊ณผ ๊ฐ™์Œ.

@Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) {
  LocalSessionFactoryBean sfb = new LocalSessionFactoryBean();
  sfb.setDataSource(dataSource); // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜ ๋ฐ ์ข…๋ฅ˜ ํ™•์ธ
  sfb.setPackagesToScan(new String[] { "com.bobfull.cafe.domain" }); // ํผ์‹œ์Šคํ„ด์Šค ์• ๋„ˆํ…Œ์ด์…˜์ด ์ ์šฉ๋œ ๋„๋ฉ”์ธ ํด๋ž˜์Šค(@Entity, @MappedSuperClass ํฌํ•จ)
  Properties props = new Properties();
  props.setProperty("dialect", "org.hiebernate.dialect.H2Dialect"); 
  sfb.setHibernateProperties(props); // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜ ๋ฐ ์ข…๋ฅ˜ ํ™•์ธ
  return sfb;
}


์Šคํ”„๋ง์œผ๋กœ๋ถ€ํ„ฐ ํ•ด๋ฐฉ๋œ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ๊ตฌ์„ฑ

ํ•˜์ด๋ฒ„๋„ค์ด์Šค ์ƒํ™ฉ์„ธ์…˜์„ ์ด์šฉํ•˜์—ฌ, HibernateTemplate์ด ํ•„์š” ์—†์ด, SessionFactory๋ฅผ ์ง์ ‘ ์ €์žฅ์†Œ์— ์™€์ด์–ด๋ง ํ•  ์ˆ˜ ์žˆ๋‹ค.

@Repository
public class HibrernateCoffeeRepository implements CoffeeRepository{

    private SessionFactory sessionFactory 
    
    @Inject
    public HibernateCoffeeRepository(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory; //์„ธ์…˜ํŒฉํ† ๋ฆฌ ์ฃผ์ž…
    }

    private Session currentSession() {
        return sessionFactory.getCurrentSession(); // ํ˜„์žฌ ์„ธ์…˜ ์ถ”์ถœ
    }
    
    public long count() {
        return findAll().size();
    }
    public Coffee save(Coffee coffee) {
        Serializable id = currentSession().save(Coffee); // ํ˜„์žฌ ์„ธ์…˜ ์‚ฌ์šฉ
        return new Coffee((Long) id,
                Coffee.getName(),
                Coffee.getWater(),
                Coffee.getShot());
    }

    public Coffee findOne(long id) {
        return (Coffee) currentSession().get(Coffee.class, id);
    }

    public Coffee findByName(String name) {
        return (Coffee) currentSession()
                .createCriteria(Coffee.class)
                .add(Restrictions.eq("name", name))
                .list().get(0);
    }

    public List<Coffee> findAll() {
        return (List<Coffee>) currentSession()
                .createCriteria(Coffee.class).list();
  } 
}

=> sessionFactory ํ”„๋กœํผํ‹ฐ์—์„œ, SessionFactory๋ฅผ ์ž๋™ ์ฃผ์ž…ํ•œ ํ›„, currentSession() ๋ฉ”์†Œ๋“œ์—์„œ ์ด SessionFactory๋ฅผ ์ด์šฉํ•ด์„œ ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์˜ ์„ธ์…˜์„ ์–ป์Œ

=> @Repository ์• ๋„ˆํ…Œ์ด์…˜์„ ์ ์šฉํ•˜์—ฌ, ์ปดํฌ๋„ŒํŠธ ์Šค์บ๋‹์— ์Šค์บ”๋จ(HibernateCoffeeRepository ๋นˆ์ด ํ•„์š” ์—†์Œ), ๋˜ํ•œ ๋ช…์‹œ์  ์„ค์ •์„ ์ค„์ด๋Š”๋ฐ์— ๋„์›€์„ ์ค€๋‹ค

* ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ํ…œํ”Œ๋ฆฟ์ด ์•„๋‹Œ, ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์ €์žฅ์†Œ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ์˜ ์˜ˆ์™ธ ๋ณ€ํ™˜์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ด์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค

// ํ…œํ”Œ๋ฆฟ์ด ์—†๋Š” ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์ €์žฅ์†Œ์— ์˜ˆ์™ธ ๋ณ€ํ™˜ ์ถ”๊ฐ€
@Bean
public BeanPostProcessor perstistenceTeanslation() {
  return new PersistenceExceptionTeanslationPostProcessor(); //๋นˆ์˜ ํ›„ ์ฒ˜๋ฆฌ๊ธฐ, @Repository์— ์ถ”๊ฐ€ํ•˜์—ฌ, ํ”Œ๋žซํผ์— ํŠนํ™”๋œ ๋ชจ๋“  ์˜ˆ์™ธ๋ฅผ ์žก์€ ํ›„ ๋น„๊ฒ€์‚ฌํ˜• ์˜ˆ์™ธ๋กœ ๋˜์ง 
}

์Šคํ”„๋ง๊ณผ ์ž๋ฐ” ํผ์‹œ์Šคํ„ด์Šค API

JPA(java persitence API)๋Š” POJO ๊ธฐ๋ฐ˜์˜ ํผ์‹œ์Šคํ„ด์Šค ๋ฉ”์ปค๋‹ˆ์ฆ˜,, ์Šคํ”„๋ง 2.0๋ถ€ํ„ฐ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.


์—”ํ‹ฐํ‹ฐ ๊ด€๋ฆฌ์ž ํŒฉํ† ๋ฆฌ ์„ค์ •

JPA๋กœ ๋งŒ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€, EntityManagerFactory์˜ ๊ตฌํ˜„๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด์„œ, EntityManager์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ํš๋“ํ•ด์•ผํ•œ๋‹ค.

์—”ํ‹ฐํ‹ฐ ๊ด€๋ฆฌ์ž์˜ ์ข…๋ฅ˜

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ด€๋ฆฌํ˜•_Application-managed : ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์—”ํ‹ฐํ‹ฐ ๊ด€๋ฆฌ์ž ํŒฉํ† ๋ฆฌ์—์„œ ์ง์ ‘ ์š”์ฒญํ•˜์—ฌ, ์ƒ์„ฑ๋˜๋Š” ์œ ํ˜•. ์ง์ ‘ open๋ถ€ํ„ฐ close๊นŒ์ง€ ์‹ ๊ฒฝ์จ์•ผํ•˜๋ฏ€๋กœ , ์ž๋ฐ” EE์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์—†์ด ์‹คํ–‰๋˜๋Š”, ๋…๋ฆฝํ˜• ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ ํ•ฉํ•˜๋‹ค.

  • ์ปจํ…Œ์ด๋„ˆ ๊ด€๋ฆฌํ˜•_container-managed : ์ž๋ฐ” EE ์ปจํ…Œ์ด๋„ˆ์— ์˜ํ•ด ์ƒ์„ฑ๋˜๊ณ  ๊ด€๋ฆฌ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ง์ ‘ ์—”ํ‹ฐํ‹ฐ๊ด€๋ฆฌ์ž์™€ ์ƒํ˜ธ์ž‘์šฉ ํ•˜์ง€ ์•Š์Œ. ์ข…์†๊ฐ์ฒด ์ฃผ์ž… ํ˜น์€ JNDI๋ฅผ ํ†ตํ•ด ํš๋“. ์ด ์œ ํ˜•์€ ๋‹จ์ˆœํžˆ persistnece.xml๋กœ ์ง€์ •ํ•œ ๊ฒƒ ์ด์ƒ์˜ ์ž๋ฐ” EE์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ œ๊ณตํ•˜๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉ

=> ์Šคํ”„๋ง์—์„œ๋Š”, ๊ฐœ๋ฐœ์ž์—๊ฒŒ๋Š” ํฐ ์˜๋ฏธ๊ฐ€ ์—†์Œ.. ์–ด๋–ค ์œ ํ˜•์„ ์‚ฌ์šฉํ•ด๋„, ์Šคํ”„๋ง์ด ๊ด€๋ฆฌ๋ฅผ ํ•ด์คŒ... JpaTemplate์ด ์„ธ๋ถ€์‚ฌํ•ญ์„ ๊ฐ์ถฐ์คŒ


์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ด€๋ฆฌํ˜• JPA ๊ตฌ์„ฑ

- ๋Œ€๋ถ€๋ถ„ persitence.xml ์„ค์ •ํŒŒ์ผ๋กœ ๊ฐ€์ ธ์˜ด(classpath:META-INF ๋””๋ ‰ํ† ๋ฆฌ) -> ํ•˜๋‚˜ ์ด์ƒ์˜ ํผ์‹œ์Šคํ„ด์Šค ์œ ๋‹›(ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ์†Œ์Šค๋ฅผ ๊ณต์œ ํ•˜๋Š” ํผ์‹œ์Šคํ„ด์Šค ํด๋ž˜์Šค์˜ ์ง‘ํ•ฉ) ์„ ์–ธ์ด ๋ชฉํ‘œ

 <persistence xmlns="http://java.sun.com/xml/ns/persistence"version="1.0">
    <persistence-unit name="cafePU">
      <class>com.bobfull.cafe.domain.Coffee</class>
      <class>com.bob.full.cafe.domain.CafeUser</class>
      <properties>
        <property name="toplink.jdbc.driver"
            value="org.hsqldb.jdbcDriver" />
        <property name="toplink.jdbc.url" value=
            "jdbc:hsqldb:hsql://localhost/cafe/coffee" />
        <property name="toplink.jdbc.user"
            value="sa" />
        <property name="toplink.jdbc.password"
            value="" />
      </properties>
    </persistence-unit>
  </persistence>

=> ์—ฌ๋Ÿฌ๊ฐœ์˜ ํผ์‹œ์Šคํ„ด์Šค ํด๋ž˜์Šค์™€, ๋งคํ•‘ํŒŒ์ผ ์ •๋ณด

์œ„์˜ ํŒŒ์ผ์„ ๊ฐ€์ง€๊ณ , LocalEntityManagerFactoryBean์„ ์„ ์–ธํ•ด์ค€๋‹ค.

@Bean
public LocalEntityManagerFactoryBean entityManagerFactoryBean() {
  LocalEntityManagerFactoryBean emfb
      = new LocalEntityManagerFactoryBean();
  emfb.setPersistenceUnitName("cafePU");
  return emfb;
}

==> ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ด€๋ฆฌํ˜•์˜ ๊ฒฝ์šฐ <persistence.xml>์— ํ•„์š”ํ•œ ๋‚ด์šฉ์„ ๋„ฃ์–ด๋‘์–ด์•ผ, PersistenceProvider๊ฐ€ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์Šคํ”„๋ง์„ ์ด์šฉํ•˜๋ฉด, JpaTemplate์ด PersistenceProvider์™€ ์ƒํ˜ธ์ž‘์šฉ์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์‹ค์ œ๋กœ ์„ค์ •์ •๋ณด๋ฅผ persistence.xml์— ๋„ฃ๋Š” ๊ฒƒ์€ ํ˜„๋ช…ํ•˜์ง€ ์•Š๋‹ค.


์ปจํ…Œ์ด๋„ˆ ๊ด€๋ฆฌํ˜• JPA ๊ตฌ์„ฑ

* EntityManagerFactory๋Š” ์ปจํ…Œ์ด๋„ˆ(์Šคํ”„๋ง)์ด ์ œ๊ณตํ•˜๋Š” ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ƒ์„ฑ

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
        DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
  LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
  emfb.setDataSource(dataSource);
  emfb.setJpaVendorAdapter(jpaVendorAdapter);
  emfb.setPackagesToScan("com.bobfull.cafe.domain"); // @Entity๋กœ ์• ๋„ˆํ…Œ์ด์…˜๋œ ํด๋ž˜์Šค๋ฅผ ์œ„ํ•œ ํŒจํ‚ค์ง€ ์Šค์บ” -> xml์— ๋ช…์‹œ์ ์œผ๋กœ ์„ ์–ธํ•  ํ•„์š”๊ฐ€ ์—†์Œ
  return emfb;
}

* dataSource๋ฅผ ์Šคํ”„๋ง ์ปจํ…์ŠคํŠธ์—์„œ ์ง€์ •๊ฐ€๋Šฅ ํ•˜๋‹ค

* persistence.xml์ด ํ•„์š”๊ฐ€ ์—†์Œ

* jpaVendorAdapter์˜ ๊ฒฝ์šฐ, ํŠน์ • JPA ๊ตฌํ˜„์ฒด์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค. EclipseLinkJpaVendorAdpater, HibernateJpaVendorAdapter, OpenJpaVendorAdapter๊ฐ€ ์žˆ๋‹ค.

@Bean 
public JpaVendorAdapter jpaVendorAdapter() {
  HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter(); // ๊ตฌํ˜„์ฒด๊ฐ€ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ํ•ด๋‹น ์–ด๋Œ‘ํ„ฐ๋กœ ๊ฒฐ์ •
  adapter.setDatabase("HSQL");
  adapter.setShowSql(true);
  adapter.setGenerateDdl(false);
  adapter.setDatabasePlatform("org.hibernate.dialect.HSQLDialect");
  return adapter;
}

JNDI์—์„œ EntityManagerFactory ๊ฐ€์ ธ์˜ค๊ธฐ

์„œ๋ฒ„์— ๋ฐฐํฌํ•˜๋Š” ๊ฒฝ์šฐ, EntityManagerFactory๋Š” ์ด๋ฏธ ์ƒ์„ฑ ๋๊ณ , ๊ฒ€์ƒ‰์„ ์œ„ํ•ด JNDI์—์„œ ๋Œ€๊ธฐ. ์ด ๊ฒฝ์šฐ ์Šคํ”„๋ง์˜ jee ๋„ค์ž„์ŠคํŽ˜์ด์Šค๊ฐ€ <jee:jndi-lookup> ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ๊ฐ€์ง„๋‹ค.

<jee:jndi-lookup id="emf" jndi-name="persistence/cafePU" />

์ด๋Ÿฌํ•œ ์„ค์ •์„ ๊ฐ€์ง€๊ณ , ๋นˆ์„ ์„ค์ •

@Bean
public JndiObjectFactoryBean entityManagerFactory() {}
JndiObjectFactoryBean jndiObjectFB = new JndiObjectFactoryBean();
  jndiObjectFB.setJndiName("jdbc/CafeDS");
  return jndiObjectFB;
}

JPA ๊ธฐ๋ฐ˜ ์ €์žฅ์†Œ ์ž‘์„ฑ

์Šคํ”„๋งJPA ํ†ตํ•ฉ์€ , JpaTemplate๊ณผ, JpaDaoSupprotํด๋ž˜์Šค์— ํฌํ•จ๋จ. ๊ทธ๋Ÿผ์—๋„ ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜์˜ JPA๋Š” ์ˆœ์ˆ˜ JPA ์ ‘๊ทผ ๋ฐฉ์‹์„ ์œ„ํ•ด ๋ณ„๋„๋กœ ๋‚จ๊ฒจ๋‘์—ˆ๋‹ค.

์Šคํ”„๋ง์— ํ•ด๋ฐฉ๋œ JPA

@Repository
@Transactional
public class JpaCafeRepository implements CafeRepository {
    @PersistenceUnit
    private EntityManagerFactory emf; // EntityManagerFactory ์ฃผ์ž…

    public void addCoffee(Coffee Coffee) {
      emf.createEntityManager().persist(Coffee); // EntityManager ์ƒ์„ฑ ๋ฐ  ์‚ฌ์šฉ
    }
    
    public Coffee getCoffeeByName(String name) {
      return emf.createEntityManager().find(Coffee.class, name);
    }
    public void saveCoffee(Coffee coffee) {
      emf.createEntityManager().merge(coffee);
    } 
    ~~~~
}

=> ์–ด๋– ํ•œ, ์Šคํ”„๋ง ํ…œํ”Œ๋ฆฟ๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ. @PersistencUnit์œผ๋กœ, EntityManagerFactory๊ฐ€ ์• ๋„ˆํ…Œ์ด์…˜ ๋˜๊ณ , ์Šคํ”„๋ง์€ ์ด๋ฅผ ์ €์žฅ์†Œ์— ์ฃผ์ž…ํ•œ๋‹ค. ์ดํ›„ JpaCafeRepository๋Š” ํ•ด๋‹น EntityManager๋ฅผ ์ƒ์„ฑ ํ›„ ์‚ฌ์šฉํ•œ๋‹ค.

=> EntityManager๊ฐ€ ๊ณ„์† ์ƒ์„ฑ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์Œ. ์Šค๋ ˆ๋“œ ์„ธ์ดํ”„ํ•˜์ง€ ์•Š๊ณ , ์ €์žฅ์†Œ์™€ ๊ฐ™์ด ๊ณต์œ  ์‹ฑ๊ธ€ํ†ค ๋นˆ์œผ๋กœ ์ฃผ์ž…๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ..


ํ”„๋ก์‹œ๋ฅผ ๊ฐ€์ง€๋Š” ์ €์žฅ์†Œ๋ฅผ EntityManager์— ์ฃผ์ž…

@Repository
@Transactional
public class JpaCafeRepository implements CafeRepository {
    
    @PersistenceContext
    private EntityManager em; // EntityManager ์ฃผ์ž…

    public void addCoffee(Coffee Coffee) {
      emf.createEntityManager().persist(Coffee); // EntityManager   ์‚ฌ์šฉ
    }
    
    public Coffee getCoffeeByName(String name) {
      return emf.createEntityManager().find(Coffee.class, name);
    }
    public void saveCoffee(Coffee coffee) {
      emf.createEntityManager().merge(coffee);
    } 
    ~~~~
}

* EntityManager์— ์ง์ ‘ ์ฃผ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ณ„์† ์ƒ์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

@PersistenceContext๋Š” EntityManager๋ฅผ ์ฃผ์ž…ํ•˜์ง€ ์•Š๋Š” ๋Œ€์‹ , ์ €์žฅ์†Œ์— ์‹ค์ œ EntityManager๋ฅผ ์ฃผ๊ณ , ๊ทธ๊ฒƒ์˜ ํ”„๋ก์‹œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์—ฐ๊ด€๋œ๊ฑฐ๋‚˜ ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ์—๋งŒ ์ƒˆ๋กœ ์ƒ์„ฑ => ์Šค๋ ˆ๋“œ ์„ธ์ดํ”„

@PersistenceUnit, @PersistenceContext๋Š” ๋ชจ๋‘ JPA์ŠคํŽ™์—์„œ ์ œ๊ณต => ์Šคํ”„๋ง์ด EntityManagerFactory, EntityManager๋ฅผ ์ดํ•ดํ•˜๊ณ  ์ฃผ์ž…ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”, PersistenceAnnotationBeanPostProcessor๋ฅผ ๊ตฌ์„ฑํ•ด์•ผ ํ•œ๋‹ค.

@Bean
public PersistenceAnnotationBeanPostProcessor paPostProcessor() {
  return new PersistenceAnnotationBeanPostProcessor();
}

๋งˆ์ง€๋ง‰์œผ๋กœ , ์˜ˆ์™ธ ๋ณ€ํ™˜์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์˜ˆ์™ธ๋ณ€ํ™˜ ํด๋ž˜์Šค๋„ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

@Bean
public BeanPostProcessor persistenceTranslation() {
  return new PersistenceExceptionTranslationPostProcessor();
}

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ ์ž๋™ JPA ์ €์žฅ์†Œ

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ, ๋™์ผํ•œ ์ €์žฅ์†Œ ๊ตฌํ˜„์„ ์—†์• ๊ณ  ์ €์žฅ์†Œ ์ธํ„ฐํŽ˜์ด์Šค ์ž‘์„ฑ์„ ๋ฉˆ์ถœ ์ˆ˜ ์žˆ๋‹ค.

public interface CafeRepository extends JpaRepository<Cafe, Long> {}

ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๋Š” JpaRepository๋ฅผ ํ™•์žฅํ•œ๋‹ค. ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ์—์„œ ์ €์žฅ์†Œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ฐœ๊ฒฌํ•  ๋•Œ, CafeRepository๊ตฌํ˜„์„ ์ง„ํ–‰ํ•œ๋‹ค(์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ์™€ ๋น„์Šท)

@Configuration
@EnableJpaRepositories(basePackages="com.bobfull.cafe.db")
public class JpaConfiguration {
... }

@EnableJpaRepositories() = <jpa:repositories> - ์Šคํ”„๋ง๋ฐ์ดํ„ฐ JPA์ €์žฅ์†Œ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™•์žฅํ•˜๋Š” ๊ธฐ๋ณธ ํŒจํ‚ค์ง€๋ฅผ ์Šค์บ”ํ•œ๋‹ค.

=> ์ผ๋ฐ˜ JPA๋™์ž‘์„ ์œ„ํ•œ 18๊ฐœ์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณตํ•จ


์ฟผ๋ฆฌ๋ฉ”์†Œ๋“œ ์ œ๊ณตํ•˜๊ธฐ

์œ„์˜ 18๊ฐœ ๋ฉ”์†Œ๋“œ ์ด์™ธ์—, ๋” ๋งŽ์€ ๋ฉ”์†Œ๋“œ๊ฐ€ ํ•„์š”ํ•  ๋•Œ... ์ฟผ๋ฆฌ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์Šคํ”„๋ง ๋ฐ์ดํ„ฐ๋Š” ๋ฉ”์†Œ๋“œ ๋ช…์—์„œ - ์ฟผ๋ฆฌ๋™์‚ฌ+๋Œ€์ƒ+By+์กฐ๊ฑด ์„ ํ†ตํ•ด ๋ฉ”์†Œ๋“œ๋ฅผ ๋งŒ๋“ ๋‹ค

  • ์ฟผ๋ฆฌ ๋™์‚ฌ - get, read, find, count ํ—ˆ์šฉ

  • ๋Œ€์ƒ - ๋Œ€๋ถ€๋ถ„ ๋ฌด์‹œ, JpaRespository๋ฅผ ์–ด๋–ป๊ฒŒ ํŒŒ๋ผ๋ฏธํ„ฐํ™” ํ•˜๋Š๋ƒ๊ฐ€ ๋ฌธ์ œ

  • ์กฐ๊ฑด - ๊ฒฐ๊ณผ๋ฅผ ์ œํ•œํ•˜๋Š” ํ•œ ๊ฐœ ์ด์ƒ์˜ ์กฐ๊ฑด์„ ์ฐพ์„ ์ˆ˜ ์žˆ์Œ.

์˜ˆ์‹œ : readByFirstnameOrLastname(String first, String last)

=> Stringํ”„๋กœํผํ‹ฐ ์‚ฌ์šฉ ์‹œ, ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ์ƒ๊ด€ํ•˜์ง€ ์•Š๋Š” IgnoringCase๋ฅผ ํฌํ•จํ•œ๋‹ค. readByFirstnameIgnoringCaseOrLastnameIgnoringCase(String first, String last)

* ์ตœ์ข…์ ์œผ๋กœ OrderBy๋กœ ์ •๋ ฌ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค readByFirstnameOrLastnameOrderByLastnameAsc(String first, String last)

==> ์‚ฌ์šฉ์ž๊ฐ€ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ์˜ ๋ช…๋ช…๊ทœ์น™์— ๋งž์ถฐ์„œ ๋ฉ”์†Œ๋“œ์˜ ํ™•์ • ๋ฆฌ์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅ.


๋งž์ถคํ˜• ์ฟผ๋ฆฌ ์„ ์–ธ

* ๋ฐ์ดํ„ฐ ๋ช…๋ช…๊ทœ์น™์„ ์ œ์™ธํ•˜๊ณ , ๊ตฌํ˜„์ฒด๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•˜์—ฌ @Query ์• ๋„ˆํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

@Query("select * from Coffee c where c.name like '%latte'")
List<Coffee> findAllLattees();

=> ํ•ด๋‹น ์• ๋„ˆํ…Œ์ด์…˜์„ ํ†ตํ•ด, ์ง์ ‘ ์ฟผ๋ฆฌ๋ฅผ ์งœ์„œ ๊ตฌํ˜„๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.. ํ•˜์ง€๋งŒ ์•„์ง์€ ๋ฉ”์†Œ๋“œ ๊ตฌํ˜„์€ ํ•  ์ˆ˜ ์—†๋‹ค. ๋‹จ์ง€ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA์— ๋ฉ”์†Œ๋“œ ๊ตฌํ˜„์— ๋Œ€ํ•œ ํžŒํŠธ๋ฅผ ์ฃผ๊ธฐ ์œ„ํ•œ ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์„ ๋ฟ

* ๋ช…๋ช…๊ทœ์น™์ด ๋„ˆ๋ฌด ๋ณต์žกํ•˜๊ฑฐ๋‚˜, ๋ช…๋ช…๊ทœ์น™์œผ๋กœ ์ˆ˜ํ–‰์ด ๋ถˆ๊ฐ€๋Šฅํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹๋‹ค.


๋งž์ถคํ˜• ๊ธฐ๋Šฅ ํ˜ผํ•ฉ

* ๋ช…๋ช…๊ทœ์น™๊ณผ, ๋‹จ์ผ ์ฟผ๋ฆฌ(@Query)๋กœ๋„ ํž˜๋“  ์ €์žฅ์†Œ์˜ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ๋•Œ ์˜›๋‚  ๋ฐฉ์‹์ธ, EntityManager๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•ด์•ผํ•  ์ˆ˜ ์žˆ๋‹ค ==> ์‹ค์ œ๋กœ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๊ฐ€ ํ• ์ˆ˜ ์—†๋Š” ์ผ์„ ํ•  ๋•Œ, ๋‚ฎ์€ ์ˆ˜์ค€์—์„œ JPA์™€ ํ•จ๊ป˜ ๋™์ž‘ํ•˜๋„๋ก ํ•ด์•ผํ•œ๋‹ค.

=> ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ๋ฅผ ์™„์ „ํžˆ ํฌ๊ธฐํ•  ํ•„์š”๋Š” ์—†์Œ

์Šคํ”„๋ง JPA๋Š” ์ €์žฅ์†Œ ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„์„ ์ƒ์„ฑ ํ›„, Impl์ ‘๋ฏธ์–ด๋ฅผ ๊ฐ€์ง€๋Š” ํด๋ž˜์Šค๋ฅผ ์ฐพ์Œ. ํ•ด๋‹น ํด๋ž˜์Šค์™€ JPA์— ์˜ํ•ด ์ƒ์„ฑ๋œ ๋ฐฉ๋ฒ•๊ณผ ๋ณ‘ํ•ฉ

public class CoffeeRepositoryImpl implements CoffeePriceAdder {
  @PersistenceContext
  private EntityManager em;
  public int priceAdd() {
    String update =
        "UPDATE Coffee coffee " +
        "SET coffee.price = '5000' " +
        "WHERE coffee.price = '4000' " +
        "AND coffee.id IN (" +
        "SELECT c FROM CoffeeData c WHERE (" +
        "  SELECT COUNT(sellAmount) FROM s.coffees coffees) > 10000" +
        ")";
    return em.createQuery(update).executeUpdate();
  }
}

=> ๊ธฐ์ฃผ์ž… ๋œ, EntityManager๋ฅผ ์‚ฌ์šฉ. CoffeeRepository๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์Œ!..

// CoffeePriceAdder์˜ ์ธํ„ฐํŽ˜์ด์Šค
public interface CoffeePriceAdder{ int priceAdd(); }


// CafeRepository๋ฅผ ํ™•์žฅ - ์ฝ”๋“œ ์ค‘๋ณต์„ ํ”ผํ•˜๋Š” ๋ฐฉ๋ฒ•
public interface CafeRepository extends JpaRepository<Cafe, Long> {}, CoffeePriceAdder { ~~ }

=> ๋‘˜์ค‘ ํ•˜๋‚˜์˜ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉ


... ๋ฒˆ์™ธ๋กœ impl์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์ง€ ์•Š๋‹ค๋ฉด?

 @EnableJpaRepositories(
          basePackages="com.bobfull.cafe.db",
          repositoryImplementationPostfix="Helper")

=> Postfix๋ฅผ Helper๋กœ ์ง€์ •ํ•œ ์ผ€์ด์Šค

Last updated

Was this helpful?