JPA 实体未保存在数据库中

Posted

技术标签:

【中文标题】JPA 实体未保存在数据库中【英文标题】:JPA Entities not saved in the database 【发布时间】:2019-11-05 09:01:06 【问题描述】:

我有一个使用 JPA 和 mysql 在 WildFly 10.1.0 上运行的 JEE 应用程序。

我有一个无状态服务,它代表一个网络爬虫,它从给定的网站中提取内容,创建结构化的 POJO 实体并将这些实体保存在数据库中。我的问题是,虽然没有抛出异常,但 JPA 实体并没有保存在数据库中。 我的无状态网络爬虫:

@Stateless
public class ImmoweltBot 

    public static final String URL = "https://www.immowelt.at/";
    public static final String queryURL = URL + "/liste/wien/wohnungen/mieten?sort=relevanz";


    @Inject
    private HomeToDealDBService homeToDealDBService;


    @PostConstruct
    public void initialize() 
        System.setProperty("webdriver.chrome.driver", "C:\\Temp\\chromedriver.exe");
    


    public void crawl () throws Exception 

        String URLPage = StringUtils.EMPTY;
        int page = 1;
        int totalNumberOfEntities = 6000;
        int numberOfEntitiesFound = 0;

        List<WebElement> elemnts = new ArrayList<>();

        WebDriver webDriver = new ChromeDriver();

        outer:
        while (numberOfEntitiesFound < totalNumberOfEntities)

        webDriver.get(queryURL + URLPage);      


        final int totalNumberOfKeyDowns = 190;
        int keyDownTries = 0;
        while ((++keyDownTries < totalNumberOfKeyDowns)) 
            webDriver.findElement(By.tagName("body")).sendKeys(Keys.DOWN);

        
        elemnts = webDriver.findElements(By.xpath("//*[contains(@class, 'clear relative js-listitem')]"));

        WebElement elem = webDriver.findElement(By.xpath("//*[contains(@class, 'ellipsis margin_none')]"));
        totalNumberOfEntities = Utils.parseNumber(elem.getText()).intValue();

        for (int i = 0; i < elemnts.size(); i++) 
            WebElement divListItemClear = elemnts.get(i);
            HomeToDeal homeToRent = new HomeToDeal();
            String exposeURL = divListItemClear.findElement(By.tagName("a")).getAttribute("href");
            homeToRent.setURL(exposeURL);

            try 
                homeToDealDBService.saveEntity(homeToRent);
                Logger.getLogger(ImmoweltBot.class).info("Entity " +  homeToRent + " saved");
            
            catch (EntityExistsException eex)
                Logger.getLogger(ImmoweltBot.class).warn("Entity already saved " + homeToRent.getURL());
                break outer;
            
        
        URLPage = "&cp="+ (++page);
        numberOfEntitiesFound+=elemnts.size();
     
    

我的 HomeToDealDBService 类:

@Stateless
public class HomeToDealDBService 

    @PersistenceContext
    private EntityManager em;


    @TransactionAttribute(TransactionAttributeType.REQUIRED) 
    public void saveEntity(HomeToDeal homeToDeal) throws EntityExistsException
        this.em.persist(homeToDeal);
    


我的 POJO 类:

@Entity
@Table(name = "home")
@Access(AccessType.FIELD)
public class HomeToDeal implements Comparable<HomeToDeal>, Serializable 

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @Column(name = "id", updatable=false, nullable=false)
    private int id;
    private String URL;
    private String mainImage;
    private String imageFolderPath;
    private String description;
    private byte noRooms;
    private BigDecimal nutzflaeche; // area in sqm
    private BigDecimal wohnflaeche;

我的触发爬虫的servlet:

@WebServlet("/test")
public class TestServlet extends HttpServlet 

    private static final long serialVersionUID = 1L;

    @Inject
    private ImmoweltBot immoweltBot;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException 
        try 
            immoweltBot.crawl();
         catch (Exception e) 
            throw new ServletException(e);
        
    


我的persistence.xml:

<?xml version="1.0" encoding="UTF-8" ?>
    <persistence xmlns="http://java.sun.com/xml/ns/persistence"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
     http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

       <persistence-unit name="IamOK" transaction-type="JTA">
         <description>Home to Deal</description>
         <provider>org.hibernate.ejb.HibernatePersistence</provider>

         <jta-data-source>java:/MySqlDS_HomeToDeal</jta-data-source> 
         <properties>
                <property name="hibernate.dialect" value = "org.hibernate.dialect.MySQLDialect" />
                <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>  
                <property name="hibernate.show_sql" value="true"></property>  
                <property name="hibernate.max_fetch_depth" value="3" />
                <property name="hibernate.hbm2ddl.auto" value="create"/> 
                <property name="hibernate.show_sql" value="true" />
            </properties>
       </persistence-unit>

    </persistence>

WildFly Standalone.xml 中的我的数据源:

    <datasource jta="true" jndi-name="java:/MySqlDS_HomeToDeal" pool-name="MySqlDS_HomeToDeal" enabled="true" use-java-context="true" use-ccm="true">
                <connection-url>jdbc:mysql://localhost:3306/homeToDeal?autoReconnect=true</connection-url>
                <driver>mysql</driver>
                <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
                <pool>
                    <min-pool-size>10</min-pool-size>
                    <max-pool-size>100</max-pool-size>
                    <prefill>true</prefill>
                </pool>
                <security>
                    <user-name>homeToDealuser</user-name>
                    <password>homeToDeal123</password>
                </security>
                <validation>
                    <check-valid-connection-sql>SELECT 1</check-valid-connection-sql>
                </validation>
                <timeout>
                    <idle-timeout-minutes>0</idle-timeout-minutes>
                    <query-timeout>600</query-timeout>
                </timeout>
                <statement>
                    <prepared-statement-cache-size>100</prepared-statement-cache-size>
                    <share-prepared-statements>true</share-prepared-statements>
                </statement>
            </datasource>

数据库中的结果:

select * from home;

我的 pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>seller</groupId>
    <artifactId>home.digest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>home.digest Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://maven.apache.org</url>
    <repositories>
        <repository>
            <id>prime-repo</id>
            <name>Prime Repo</name>
            <url>http://repository.primefaces.org</url>
        </repository>
    </repositories>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>3.141.59</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
            <version>4.4.10</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.6</version>
        </dependency>
        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>primefaces</artifactId>
            <version>6.2</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.logging</groupId>
            <artifactId>jboss-logging</artifactId>
            <version>3.0.1.GA</version>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.hibernate.javax.persistence/hibernate-jpa-2.1-api -->
        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.1-api</artifactId>
            <version>1.0.2.Final</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/javax.enterprise/cdi-api -->
        <dependency>
            <groupId>javax.enterprise</groupId>
            <artifactId>cdi-api</artifactId>
            <version>2.0.SP1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.as</groupId>
            <artifactId>jboss-as-web</artifactId>
            <version>7.1.1.Final</version>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jboss.spec.javax.ejb/jboss-ejb-api_3.2_spec -->
        <dependency>
            <groupId>org.jboss.spec.javax.ejb</groupId>
            <artifactId>jboss-ejb-api_3.2_spec</artifactId>
            <version>1.0.2.Final</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.google.cloud/google-cloud-translate -->
        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>google-cloud-translate</artifactId>
            <version>1.61.0</version>
        </dependency>
        <dependency>
            <!-- jsoup html parser library @ https://jsoup.org/ -->
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>home.digest</finalName>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven 
                defaults (may be moved to parent pom) -->
            <plugins>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

如果我只像这样更改我的 TestServlet:

@WebServlet("/test")
public class TestServlet extends HttpServlet 

    @Inject
    private HomeToDealDBService homeToDealDBService;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException 
        try 
            HomeToDeal home = new HomeToDeal();
            home.setURL("alabale");
            homeToDealDBService.saveEntity(home);
         catch (Exception e) 
            throw new ServletException(e);
        
    

虽然有效。可能是什么问题?

【问题讨论】:

【参考方案1】:

这种情况下的问题是注释

@TransactionAttribute(TransactionAttributeType.REQUIRED)

在方法之上

public void saveEntity(HomeToDeal homeToDeal) throws EntityExistsException
    this.em.persist(homeToDeal);

在这种情况下,当从方法 ImmoweltBot.crawl() 调用 saveEntity() 时,实际上是从同一事务中调用,直到方法 ImmoweltBot.crawl() 完成。这就是为什么在事务结束之前实体没有保存在数据库中的原因 - 意味着在 ImmoweltBot.crawl() 方法完成之前。使每个实体分别保存意味着将每个实体保存在自己的事务中。如果我将方法 saveEntity() 的注释更改如下:

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
public void saveEntity(HomeToDeal homeToDeal) throws EntityExistsException
    this.em.persist(homeToDeal);

这实际上正是我想要的——如果一个特定实体的保存失败,这不会影响其他实体的保存。

【讨论】:

【参考方案2】:

&lt;property name="hibernate.hbm2ddl.auto" value="update"/&gt; 可以解决问题。

如果您输入value="create",这意味着您每次运行应用程序时都会一次又一次地创建表。

【讨论】:

Hoq:谢谢,但这并不能解决我的问题,请参阅上述问题末尾的更新。 @K.Nicholas 我创建了另一个爬虫类,那里也有同样的情况

以上是关于JPA 实体未保存在数据库中的主要内容,如果未能解决你的问题,请参考以下文章

jpa 刷新的实体未反映在数据库中

Spring Data Jpa Repository 未将实体保存到数据库

JPA 查询未在列表中返回更新的结果

Spring JPA实体不保存到数据库

Cascade保存子实体失败了JPA(Spring数据+ Hibernate)?

JPA 实体映射