Tạo JPA EntityManager mà không cần tệp cấu hình Persence.xml


82

Có cách nào để khởi tạo EntityManagermà không có đơn vị bền vững được xác định không? Bạn có thể cung cấp tất cả các thuộc tính cần thiết để tạo trình quản lý thực thể không? Tôi cần tạo EntityManagertừ các giá trị được chỉ định của người dùng trong thời gian chạy. Cập nhật persistence.xmlvà biên dịch lại không phải là một tùy chọn.

Bất kỳ ý tưởng về cách làm điều này đều được hoan nghênh hơn cả!

Câu trả lời:


57

Có cách nào để khởi tạo EntityManagermà không có đơn vị bền bỉ được xác định không?

Bạn nên xác định ít nhất một đơn vị độ bền trong bộ persistence.xmlmô tả triển khai.

Bạn có thể cung cấp tất cả các thuộc tính cần thiết để tạo một Entitymanager?

  • Thuộc tính tên là bắt buộc. Các thuộc tính và phần tử khác là tùy chọn. (Đặc tả JPA). Vì vậy, đây ít nhiều phải là persistence.xmltệp tối thiểu của bạn :
<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        SOME_PROPERTIES
    </persistence-unit>
</persistence>

Trong môi trường Java EE, các phần tử jta-data-sourcenon-jta-data-sourceđược sử dụng để chỉ định tên JNDI chung của nguồn dữ liệu JTA và / hoặc không phải JTA sẽ được sử dụng bởi nhà cung cấp lâu dài.

Vì vậy, nếu Máy chủ ứng dụng mục tiêu của bạn hỗ trợ JTA (JBoss, Websphere, GlassFish), bạn persistence.xmlsẽ trông giống như sau:

<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        <!--GLOBAL_JNDI_GOES_HERE-->
        <jta-data-source>jdbc/myDS</jta-data-source>
    </persistence-unit>
</persistence>

Nếu Máy chủ ứng dụng mục tiêu của bạn không hỗ trợ JTA (Tomcat), bạn persistence.xmlsẽ trông giống như sau:

<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        <!--GLOBAL_JNDI_GOES_HERE-->
        <non-jta-data-source>jdbc/myDS</non-jta-data-source>
    </persistence-unit>
</persistence>

Nếu nguồn dữ liệu của bạn không bị ràng buộc với JNDI chung (ví dụ: bên ngoài vùng chứa Java EE), thì bạn thường sẽ xác định các thuộc tính JPA nhà cung cấp, trình điều khiển, url, người dùng và mật khẩu. Nhưng tên tài sản phụ thuộc vào nhà cung cấp JPA. Vì vậy, đối với Hibernate với tư cách là nhà cung cấp JPA, persistence.xmltệp của bạn sẽ giống như sau:

<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>br.com.persistence.SomeClass</class>
        <properties>
            <property name="hibernate.connection.driver_class" value="org.apache.derby.jdbc.ClientDriver"/>
            <property name="hibernate.connection.url" value="jdbc:derby://localhost:1527/EmpServDB;create=true"/>
            <property name="hibernate.connection.username" value="APP"/>
            <property name="hibernate.connection.password" value="APP"/>
        </properties>
    </persistence-unit>
</persistence>

Thuộc tính loại giao dịch

Nói chung, trong môi trường Java EE, kiểu giao dịch RESOURCE_LOCALgiả định rằng một nguồn dữ liệu không phải JTA sẽ được cung cấp. Trong môi trường Java EE, nếu phần tử này không được chỉ định, thì mặc định là JTA. Trong môi trường Java SE, nếu phần tử này không được chỉ định, thì giá trị mặc định RESOURCE_LOCALcó thể được giả định.

  • Để đảm bảo tính di động của ứng dụng Java SE, cần phải liệt kê rõ ràng các lớp độ bền được quản lý được bao gồm trong đơn vị độ bền (đặc tả JPA)

Tôi cần tạo EntityManagertừ các giá trị được chỉ định của người dùng trong thời gian chạy

Vì vậy, hãy sử dụng cái này:

Map addedOrOverridenProperties = new HashMap();

// Let's suppose we are using Hibernate as JPA provider
addedOrOverridenProperties.put("hibernate.show_sql", true);

Persistence.createEntityManagerFactory(<PERSISTENCE_UNIT_NAME_GOES_HERE>, addedOrOverridenProperties);

Hi Tôi cố gắng giải pháp của bạn, nhưng gặp sự cố, bạn có thể xin vui lòng kiểm tra câu hỏi của tôi: stackoverflow.com/questions/3935394/...
stacker

Nhưng ... câu hỏi đặt ra là làm thế nào để tạo JPA EntityManager mà không cần đến Persence.xml . Câu trả lời này là tốt, nhưng nó vẫn đang sử dụng Persence.xml, phải không?
Joshua Davis

Trong Môi trường JavaEE, việc tạo EntityManagerFactory có được quản lý bởi EJB / JPA không?
Anthony Vinay

29

Có, bạn có thể mà không cần sử dụng bất kỳ tệp xml nào bằng cách sử dụng spring như thế này bên trong lớp @Configuration (hoặc xml cấu hình mùa xuân tương đương của nó):

@Bean
public LocalContainerEntityManagerFactoryBean emf(){
    properties.put("javax.persistence.jdbc.driver", dbDriverClassName);
    properties.put("javax.persistence.jdbc.url", dbConnectionURL);
    properties.put("javax.persistence.jdbc.user", dbUser); //if needed

    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
    emf.setPersistenceProviderClass(org.eclipse.persistence.jpa.PersistenceProvider.class); //If your using eclipse or change it to whatever you're using
    emf.setPackagesToScan("com.yourpkg"); //The packages to search for Entities, line required to avoid looking into the persistence.xml
    emf.setPersistenceUnitName(SysConstants.SysConfigPU);
    emf.setJpaPropertyMap(properties);
    emf.setLoadTimeWeaver(new ReflectiveLoadTimeWeaver()); //required unless you know what your doing
    return emf;
}

Đối tượng là propertiesgì?
ThreaT

nó là một đối tượng java.util.Properties đơn giản
Frank Orellana

20

Đây là một giải pháp mà không cần đến Spring. Các hằng số được lấy từ org.hibernate.cfg.AvailableSettings:

entityManagerFactory = new HibernatePersistenceProvider().createContainerEntityManagerFactory(
            archiverPersistenceUnitInfo(),
            ImmutableMap.<String, Object>builder()
                    .put(JPA_JDBC_DRIVER, JDBC_DRIVER)
                    .put(JPA_JDBC_URL, JDBC_URL)
                    .put(DIALECT, Oracle12cDialect.class)
                    .put(HBM2DDL_AUTO, CREATE)
                    .put(SHOW_SQL, false)
                    .put(QUERY_STARTUP_CHECKING, false)
                    .put(GENERATE_STATISTICS, false)
                    .put(USE_REFLECTION_OPTIMIZER, false)
                    .put(USE_SECOND_LEVEL_CACHE, false)
                    .put(USE_QUERY_CACHE, false)
                    .put(USE_STRUCTURED_CACHE, false)
                    .put(STATEMENT_BATCH_SIZE, 20)
                    .build());

entityManager = entityManagerFactory.createEntityManager();

Và khét tiếng PersistenceUnitInfo

private static PersistenceUnitInfo archiverPersistenceUnitInfo() {
    return new PersistenceUnitInfo() {
        @Override
        public String getPersistenceUnitName() {
            return "ApplicationPersistenceUnit";
        }

        @Override
        public String getPersistenceProviderClassName() {
            return "org.hibernate.jpa.HibernatePersistenceProvider";
        }

        @Override
        public PersistenceUnitTransactionType getTransactionType() {
            return PersistenceUnitTransactionType.RESOURCE_LOCAL;
        }

        @Override
        public DataSource getJtaDataSource() {
            return null;
        }

        @Override
        public DataSource getNonJtaDataSource() {
            return null;
        }

        @Override
        public List<String> getMappingFileNames() {
            return Collections.emptyList();
        }

        @Override
        public List<URL> getJarFileUrls() {
            try {
                return Collections.list(this.getClass()
                                            .getClassLoader()
                                            .getResources(""));
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        @Override
        public URL getPersistenceUnitRootUrl() {
            return null;
        }

        @Override
        public List<String> getManagedClassNames() {
            return Collections.emptyList();
        }

        @Override
        public boolean excludeUnlistedClasses() {
            return false;
        }

        @Override
        public SharedCacheMode getSharedCacheMode() {
            return null;
        }

        @Override
        public ValidationMode getValidationMode() {
            return null;
        }

        @Override
        public Properties getProperties() {
            return new Properties();
        }

        @Override
        public String getPersistenceXMLSchemaVersion() {
            return null;
        }

        @Override
        public ClassLoader getClassLoader() {
            return null;
        }

        @Override
        public void addTransformer(ClassTransformer transformer) {

        }

        @Override
        public ClassLoader getNewTempClassLoader() {
            return null;
        }
    };
}

3
Điều này đã giúp tôi rất nhiều vì nó giúp tôi tránh sử dụng arquillian trong một số trường hợp thử nghiệm!
cljk

18

Tôi đã có thể tạo một EntityManagervới Hibernate và PostgreSQL hoàn toàn bằng cách sử dụng mã Java (với cấu hình Spring) như sau:

@Bean
public DataSource dataSource() {
    final PGSimpleDataSource dataSource = new PGSimpleDataSource();

    dataSource.setDatabaseName( "mytestdb" );
    dataSource.setUser( "myuser" );
    dataSource.setPassword("mypass");

    return dataSource;
}

@Bean
public Properties hibernateProperties(){
    final Properties properties = new Properties();

    properties.put( "hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect" );
    properties.put( "hibernate.connection.driver_class", "org.postgresql.Driver" );
    properties.put( "hibernate.hbm2ddl.auto", "create-drop" );

    return properties;
}

@Bean
public EntityManagerFactory entityManagerFactory( DataSource dataSource, Properties hibernateProperties ){
    final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource( dataSource );
    em.setPackagesToScan( "net.initech.domain" );
    em.setJpaVendorAdapter( new HibernateJpaVendorAdapter() );
    em.setJpaProperties( hibernateProperties );
    em.setPersistenceUnitName( "mytestdomain" );
    em.setPersistenceProviderClass(HibernatePersistenceProvider.class);
    em.afterPropertiesSet();

    return em.getObject();
}

Cuộc gọi tới LocalContainerEntityManagerFactoryBean.afterPropertiesSet()là điều cần thiết vì nếu không thì nhà máy sẽ không bao giờ được xây dựng, sau đó getObject()quay trở lại nullvà bạn phải đuổi theo NullPointerExceptionsuốt cả ngày. > :-(

Sau đó, nó hoạt động với mã sau:

PageEntry pe = new PageEntry();
pe.setLinkName( "Google" );
pe.setLinkDestination( new URL( "http://www.google.com" ) );

EntityTransaction entTrans = entityManager.getTransaction();
entTrans.begin();
entityManager.persist( pe );
entTrans.commit();

Thực thể của tôi ở đâu:

@Entity
@Table(name = "page_entries")
public class PageEntry {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String linkName;
    private URL linkDestination;

    // gets & setters omitted
}

2
Thay thế tốt cho Hibernate.
javydreamercsw

8

Với JPA thuần túy, giả sử rằng bạn có một PersistenceProvidertriển khai (ví dụ: Hibernate), bạn có thể sử dụng phương thức PersistenceProvider # createContainerEntityManagerFactory (PersistenceUnitInfo info, Map map) để bootstrap một EntityManagerFactorymà không cần a persistence.xml.

Tuy nhiên, thật khó chịu khi bạn phải triển khai PersistenceUnitInfogiao diện, vì vậy tốt hơn hết bạn nên sử dụng Spring hoặc Hibernate, cả hai đều hỗ trợ bootstrapping JPA mà không cần persistence.xmltệp:

this.nativeEntityManagerFactory = provider.createContainerEntityManagerFactory(
    this.persistenceUnitInfo, 
    getJpaPropertyMap()
);

Nơi PersistenceUnitInfo được triển khai bởi lớp MutablePersistenceUnitInfo dành riêng cho Spring .

Hãy xem bài viết này để biết cách bạn có thể đạt được mục tiêu này với Hibernate.


Việc sử dụng MutablePersistenceUnitInfokhông hoạt động vì một số phương thức ném UnsupportedOperationException . Ngoài ra, bài báo được đề cập là một chút lỗi thời: getPersistenceUnitRootUrlkhông thể trả về null nếu không Hibernate không quét classpath (Hibernate 5.2.8).
Brice

1
Tôi đã sai một chút, bài viết không lỗi thời về mặt đó vì mã đang chuyển một danh sách các thực thể và không sử dụng tính năng quét gói. Tuy nhiên, để quét thực thể tự động, cần phải thực hiện getPersistenceUnitRootUrl hoặc getJarFileUrls. Sau đó được gieo trong stackoverflow.com/a/42372648/48136
Brice

0

DataNucleus JPA mà tôi sử dụng cũng có cách thực hiện việc này trong tài liệu của nó . Không cần mùa xuân, hoặc thực hiện xấu xí của PersistenceUnitInfo.

Đơn giản chỉ cần làm như sau

import org.datanucleus.metadata.PersistenceUnitMetaData;
import org.datanucleus.api.jpa.JPAEntityManagerFactory;

PersistenceUnitMetaData pumd = new PersistenceUnitMetaData("dynamic-unit", "RESOURCE_LOCAL", null);
pumd.addClassName("mydomain.test.A");
pumd.setExcludeUnlistedClasses();
pumd.addProperty("javax.persistence.jdbc.url", "jdbc:h2:mem:nucleus");
pumd.addProperty("javax.persistence.jdbc.user", "sa");
pumd.addProperty("javax.persistence.jdbc.password", "");
pumd.addProperty("datanucleus.schema.autoCreateAll", "true");

EntityManagerFactory emf = new JPAEntityManagerFactory(pumd, null);

0

Bạn cũng có thể nhận EntityManager bằng cách sử dụng PersistenceContext hoặc chú thích tự động, nhưng lưu ý rằng nó sẽ không an toàn cho chuỗi.

@PersistenceContext
private EntityManager entityManager;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.