Utwórz JPA EntityManager bez pliku konfiguracyjnego persistence.xml

82

Czy istnieje sposób na zainicjowanie EntityManagerbez zdefiniowanej jednostki trwałości? Czy możesz podać wszystkie wymagane właściwości, aby utworzyć menedżera encji? Muszę utworzyć EntityManagerz wartości określonych przez użytkownika w czasie wykonywania. Aktualizacja persistence.xmli ponowna kompilacja nie są opcją.

Każdy pomysł, jak to zrobić, jest więcej niż mile widziany!

javydreamercsw
źródło

Odpowiedzi:

57

Czy istnieje sposób na zainicjowanie EntityManagerbez zdefiniowanej jednostki trwałości?

W deskryptorze wdrażania należy zdefiniować co najmniej jedną jednostkę trwałości persistence.xml.

Czy możesz podać wszystkie wymagane właściwości, aby utworzyć Entitymanager?

  • Atrybut nazwy jest wymagany. Pozostałe atrybuty i elementy są opcjonalne. (Specyfikacja JPA). Powinien to być mniej więcej twój minimalny persistence.xmlplik:
<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        SOME_PROPERTIES
    </persistence-unit>
</persistence>

W środowiskach Java EE elementy jta-data-sourcei non-jta-data-sourcesą używane do określenia globalnej nazwy JNDI źródła danych JTA i / lub innego źródła danych, które ma być używane przez dostawcę trwałości.

Jeśli więc twój docelowy serwer aplikacji obsługuje JTA (JBoss, Websphere, GlassFish), Twój persistence.xmlwygląd będzie wyglądał następująco:

<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>

Jeśli docelowy serwer aplikacji nie obsługuje JTA (Tomcat), Twój persistence.xmlwygląd wygląda następująco:

<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>

Jeśli źródło danych nie jest powiązane z globalnym JNDI (na przykład poza kontenerem Java EE), zwykle definiuje się dostawcę JPA, sterownik, adres URL, właściwości użytkownika i hasło. Ale nazwa właściwości zależy od dostawcy JPA. Tak więc dla Hibernate jako dostawcy JPA persistence.xmlplik będzie wyglądał następująco:

<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>

Atrybut typu transakcji

Ogólnie w środowiskach Java EE typ transakcji RESOURCE_LOCALzakłada, że ​​zostanie dostarczone źródło danych inne niż JTA. W środowisku Java EE, jeśli ten element nie jest określony, wartością domyślną jest JTA. W środowisku Java SE, jeśli ten element nie jest określony, RESOURCE_LOCALmożna przyjąć wartość domyślną .

  • Aby zapewnić przenośność aplikacji Java SE, konieczne jest jawne podanie zarządzanych klas trwałości, które są zawarte w jednostce trwałości (specyfikacja JPA)

Muszę utworzyć EntityManagerz wartości określonych przez użytkownika w czasie wykonywania

Więc użyj tego:

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);
Arthur Ronald
źródło
Cześć. Wypróbowałem twoje rozwiązanie, ale napotkałem problemy, czy możesz sprawdzić moje pytanie: stackoverflow.com/questions/3935394/…
stacker
Ale ... pytanie brzmiało, jak utworzyć EntityManager JPA bez persystencji.xml. Ta odpowiedź jest dobra, ale nadal używa persystencji.xml, prawda?
Joshua Davis
Czy tworzenie EntityManagerFactory w środowisku JavaEE jest zarządzane przez EJB / JPA?
Anthony Vinay
29

Tak, możesz bez używania żadnego pliku xml, używając spring w taki sposób, jak to w klasie @Configuration (lub jej odpowiednika spring config xml):

@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;
}
Frank Orellana
źródło
Co to za przedmiot properties?
ZAGROŻENIE
jest to prosty obiekt java.util.Properties
Frank Orellana,
20

Oto rozwiązanie bez Springa. Stałe pobierane są z 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();

I niesławny 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;
        }
    };
}
Brice
źródło
3
Bardzo mi to pomogło, ponieważ pomogło mi uniknąć nadmiernego obciążenia związanego z używaniem arquillian w niektórych przypadkach testowych!
cljk
18

Udało mi się stworzyć za EntityManagerpomocą Hibernate i PostgreSQL wyłącznie przy użyciu kodu Java (z konfiguracją Spring):

@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();
}

Telefon LocalContainerEntityManagerFactoryBean.afterPropertiesSet()jest niezbędny, ponieważ w przeciwnym razie fabryka nigdy nie zostanie zbudowana, a potem getObject()wróci, nulla ty gonisz za nią przez NullPointerExceptioncały dzień. > :-(

Następnie działał z następującym kodem:

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();

Gdzie moja istota była taka:

@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
}
Sanki
źródło
2
Dobra alternatywa dla Hibernate.
javydreamercsw
8

W przypadku zwykłego JPA, zakładając, że masz PersistenceProviderimplementację (np. Hibernate), możesz użyć metody PersistenceProvider # createContainerEntityManagerFactory (PersistenceUnitInfo info, Map map) , aby załadować plik EntityManagerFactorybez konieczności używania pliku persistence.xml.

Jednak denerwujące jest to, że musisz zaimplementować PersistenceUnitInfointerfejs, więc lepiej jest użyć Spring lub Hibernate, które obsługują ładowanie JPA bez persistence.xmlpliku:

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

Gdzie PersistenceUnitInfo jest implementowana przez klasę MutablePersistenceUnitInfo specyficzną dla Spring .

Vlad Mihalcea
źródło
Używanie MutablePersistenceUnitInfonie działa, ponieważ niektóre metody zgłaszają wyjątek UnsupportedOperationException . Również wspomniany artykuł jest nieco nieaktualny: getPersistenceUnitRootUrlnie może zwrócić wartości null, w przeciwnym razie Hibernate nie skanuje ścieżki klas (Hibernate 5.2.8).
Brice,
1
Trochę się myliłem, artykuł nie jest nieaktualny pod tym względem, ponieważ kod przekazuje listę podmiotów i nie korzysta ze skanowania pakietów. Jednak w przypadku automatycznego skanowania obiektów konieczne jest zaimplementowanie opcji getPersistenceUnitRootUrl lub getJarFileUrls. Później wysiewa się na stackoverflow.com/a/42372648/48136
Brice,
0

DataNucleus JPA, którego używam, również ma sposób na zrobienie tego w swoich dokumentach . Nie potrzeba wiosny ani brzydkiej implementacji PersistenceUnitInfo.

Po prostu wykonaj następujące czynności

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);

źródło
0

Możesz również uzyskać EntityManager za pomocą adnotacji PersistenceContext lub Autowired, ale pamiętaj, że nie będzie to bezpieczne wątkowo.

@PersistenceContext
private EntityManager entityManager;
Ankit Sharma
źródło