2

Given a typical JPA example such as this one, we have code like this:

@Entity
public class Company {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    //...etc...
}

and:

public class CompanyDaoImpl implements CompanyDao {
    @PersistenceContext(unitName = "custdb")
    private EntityManager em;

    public void createCompany(final Company c) {
        em.persist(c);
    }
    //...etc...
}

(Let's ignore the @Stateless attribute for this example)

Reading other sites about anaemic domain models and this Q&A on why shouldn't JPA Entities contain business logic, why shouldn't I be able to say:

@Entity
public class Company {
    @PersistenceContext(unitName = "custdb")
    private EntityManager em;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    public void create() {
        em.persist(this);
    }
    //...etc...
}

Am I going to get myself into a world of hurt doing this?

Ken Y-N
  • 14,644
  • 21
  • 71
  • 114

2 Answers2

1

One possible reason is that instead of duplicating the CRUD (create, read, update, delete) operations in every class you may be able to create more generic solution allowing you to work with instances of multiple classes.

Have a look at this question. The accepted answer gives an example of generic DAO parametrized with the type of object it operates on. This is quite a common pattern to use.

Community
  • 1
  • 1
Bartek Maraszek
  • 1,404
  • 2
  • 14
  • 31
  • 1
    If you're going to go this route then why not just use Spring Data JPA? Gives you all the benefits of the above pattern and a whole lot more - http://spring.io/guides/gs/accessing-data-jpa – Donovan Muller Jul 01 '14 at 06:11
1

Using JPA(Hibernate) with a Spring test, I was able to show that it is possible. Assuming this is your rather enlightened entity:

models.so.haum.IntropectiveEntity.java

@Entity
public class IntrospectiveEntity {

    @Transient
    private EntityManager em;

    protected IntrospectiveEntity() { }

    public IntrospectiveEntity(final EntityManager em) {
        this.em = em;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    public void create() {
        em.persist(this);
    }

    public List<IntrospectiveEntity> all() {
        TypedQuery<IntrospectiveEntity> query = em.createQuery("SELECT ie FROM IntrospectiveEntity ie", IntrospectiveEntity.class);
        return query.getResultList();
    }
}

note that you must mark your EntityManager as @Transient otherwise JPA will try map it as a column. Also note that I couldn't get the EntityManager instance injected, so had to pass that as a constructor arg.

and this is your test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class IntrospectiveEntityTest {

    @PersistenceContext
    private EntityManager em;

    @Test
    @Transactional
    public void namaste() {
        IntrospectiveEntity introspectiveEntity = new IntrospectiveEntity(em);
        introspectiveEntity.create();

        assertThat(introspectiveEntity.all().size(), IsEqual.equalTo(1));
    }

    @Configuration
    @ComponentScan(basePackages = "so")
    static class IntrospectiveEntityConfiguration {

        @Bean
        public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
            JpaTransactionManager transactionManager = new JpaTransactionManager();
            transactionManager.setEntityManagerFactory(entityManagerFactory);
            return transactionManager;
        }

        @Bean
        public LocalContainerEntityManagerFactoryBean entityManager(DataSource dataSource) {
            LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();

            HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
            jpaVendorAdapter.setGenerateDdl(true);
            jpaVendorAdapter.setShowSql(true);
            entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter);

            entityManagerFactory.setDataSource(dataSource);
            entityManagerFactory.setPackagesToScan("so.models.haum");
            return entityManagerFactory;
        }

        @Bean
        public DataSource dataSource() {
            EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
            EmbeddedDatabase dataSource = builder.setType(EmbeddedDatabaseType.H2).build();
            return dataSource;
        }
    }
}

it successfully passes. Persisting and finding itself. So if you really want to do it this way, it is possible. Obviously the EntityManager not being injected is probably not what you wanted but there you have it.

Donovan Muller
  • 3,822
  • 3
  • 30
  • 54