天天看點

wildfly jobss 同時連接配接多個資料源 datasource xa-datasource

由于需要從一個遠端機器取資料。處理後儲存到本地資料庫處理。用 wildfly datasource 會報:

[com.arjuna.ats.arjuna] (default task-6) ARJUNA012140: Adding multiple last resources is disallowed. Trying to add LastResourceRecord(XAOnePhaseResource(LocalXAResourceImpl@7f19c56d[connectionListener=1......

這主要是jpa裡面的的事物,隻允許一個datasource連接配接。

采用xa-datasource即可。

另一種方案是在主要程式中禁止事務。将資料庫的操作寫在其他類中,使用事務。即隻有要修改的操作才需要事務。

更一步,可以在遠端取資料的類中,也是不需要事務的。這樣就可以inject 需要使用事務的類。

主要類:

package com.italktv.iof.service;

import java.util.List;
import java.util.logging.Logger;

import javax.annotation.Resource;
import javax.ejb.Schedule;
import javax.ejb.Stateless;
import javax.ejb.Timer;
import javax.ejb.TimerService;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionManagement;
import javax.inject.Inject;

import com.italktv.iof.entity.AccountInfoSync;

@Stateless
@TransactionManagement(javax.ejb.TransactionManagementType.CONTAINER)
@TransactionAttribute(javax.ejb.TransactionAttributeType.NEVER)
public class SyncTableTimer {

    @Inject
    private Logger logger;

    @Resource
    private TimerService timerService;

    @Inject
    IOFDbUtil iof;

    @Inject
    private MySqlUtil mysql;

//        @Schedule(hour = "6", minute = "0", second = "0", persistent = false ,info = "6點執行 ")
    @Schedule(hour = "*", minute = "*/1", second = "0", persistent = false, info = " ")
    public void automaticCustomer() {
        logger.info("=== job " + " started ====");
        doTask();
        logger.info("=== job end ====");
    }

    private void cancelTimers() {
        for (Timer timer : timerService.getTimers()) {
//                timer.cancel();
        }
    }

    private void doTask() {
        List<AccountInfoSync> list;
        list = iof.getList();
        while (list.size() > 0) {
            logger.info(" list.size=" + list.size());
            mysql.saveList(list);
            list = iof.getList();
        }
    }
}      

View Code

連接配接資料源1:

@Stateless
public class MySqlUtil {

    @Inject
    private Logger logger;


    @PersistenceContext(unitName = "primary")
    protected EntityManager em;

    public void saveList(List<AccountInfoSync> list) {
String dataSql = "select * " + "from profile where id=1";
        Query nativeQuery = em.createNativeQuery(dataSql, MacProfile.class);

        MacProfile i;
        try {
            i = (MacProfile) nativeQuery.getSingleResult();
//        
        } catch (NoResultException e) {
            i = new MacProfile();
        }


    }



}      

連接配接資料源2,select操作

wildfly jobss 同時連接配接多個資料源 datasource xa-datasource
@Stateless
@TransactionManagement(javax.ejb.TransactionManagementType.CONTAINER)
@TransactionAttribute(javax.ejb.TransactionAttributeType.NEVER)
public class IOFDbUtil {

    @Inject
    private Logger logger;

    @PersistenceContext(unitName = "theirDb")
    private EntityManager theirDb;

    int FETCH_MAX_SIZE = 1;
    int start = 0;

    @PostConstruct
    void init() {

    }

    public List<AccountInfoSync> getList() {

        List<AccountInfoSync> list = null;


        while (true) {
            String dataSql = "select  * from dbtable  "
                    + "where id>" + start + " and id<=" + start + "+" + FETCH_MAX_SIZE;
            Query nativeQuery = iof.createNativeQuery(dataSql, AccountInfoSync.class);


            list = (List<AccountInfoSync>) nativeQuery.getResultList();
            start += FETCH_MAX_SIZE;
            if (list.size() < 1 && start > maxid)
                break;

            if (list.size() < 1) {
                continue;
            }

            for (AccountInfoSync iofsync : list) {
                logger.info(iofsync.toString());
            }
            break;//找到就停止
        }
        return list;
    }

    @Inject
    MySqlUtil mm;

    private int getLastId() {

        mm.test();
        return 0;
    }


}      
  • 使用xa-datasource解決方案

standardalone.xml中:

<subsystem xmlns="urn:jboss:domain:datasources:4.0">
            <datasources>
                <datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
                    <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
                    <driver>h2</driver>
                    <security>
                        <user-name>sa</user-name>
                        <password>sa</password>
                    </security>
                </datasource>
                <datasource jndi-name="java:jboss/datasources/MySqlDS" pool-name="MySqlDS" enabled="true" use-java-context="true">
                    <connection-url>jdbc:mysql://localhost:3306/statisticsystem</connection-url>
                    <driver>mysql</driver>
                    <security>
                        <user-name>jboss</user-name>
                        <password>jboss</password>
                    </security>
                </datasource>
                <datasource jndi-name="java:jboss/datasources/MySqlStatBank" pool-name="MySqlStatBank" enabled="true" use-java-context="true">
                    <connection-url>jdbc:mysql://211.100.75.204:5029/statistics</connection-url>
                    <driver>mysql</driver>
                    <security>
                        <user-name>root</user-name>
                        <password>@^#coopen</password>
                    </security>
                </datasource>
                <xa-datasource jndi-name="java:jboss/datasources/MySqlStatBank1" pool-name="MySqlStatBank1" enabled="true" use-java-context="true">
                    <xa-datasource-property name="ServerName">
                        211.100.75.204
                    </xa-datasource-property>
                    <xa-datasource-property name="PortNumber">
                        5029
                    </xa-datasource-property>
                    <xa-datasource-property name="DatabaseName">
                        statistics
                    </xa-datasource-property>
                    <driver>mysql</driver>
                    <xa-pool>
                        <min-pool-size>5</min-pool-size>
                        <initial-pool-size>5</initial-pool-size>
                        <max-pool-size>100</max-pool-size>
                        <prefill>true</prefill>
                    </xa-pool>
                    <security>
                        <user-name>root</user-name>
                        <password>@^#coopen</password>
                    </security>
                </xa-datasource>
                <drivers>
                    <driver name="h2" module="com.h2database.h2">
                        <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
                    </driver>
                    <driver name="mysql" module="com.mysqldatabase.mysql">
                        <driver-class>com.mysql.jdbc.Driver</driver-class>
                        <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
                    </driver>
                </drivers>
            </datasources>
        </subsystem>      

persistent.xml:

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

    <persistence-unit name="statLog">
        <jta-data-source>java:jboss/datasources/MySqlStatBank1</jta-data-source>
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
<!--        <class>com.italktv.colnv.stat.entity.LogOfPlay</class> -->
<!--        <class>com.italktv.colnv.stat.entity.LogOfView</class> -->

        <properties>
                    <property name="hibernate.hbm2ddl.auto" value="create-drop" />
            <!-- <property name= "hibernate.hbm2ddl.auto" value ="validate" /> create-drop -->
            <property name="hibernate.jdbc.fetch_size" value="15" />
            <property name="hibernate.jdbc.batch_size" value="10" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="true"></property>

            <!-- 
            <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>  
            <property name="javax.persistence.schema-generation-target" value="scripts"/>
            <property name="javax.persistence.ddl-create-script-target" value="e:\createSeats.sql"/>
            <property name="javax.persistence.ddl-drop-script-target" value="e:/dropSeats.sql"/>
             -->
        </properties>

    </persistence-unit>

        <persistence-unit name="primary"  >
        <class>com.italktv.colnv.stat.entity.Seat</class>
        <class>com.italktv.colnv.stat.entity.SeatType</class>
        <class>com.italktv.colnv.stat.entity.LogOfPlayDuration</class> 
        <class>com.italktv.colnv.stat.entity.ReportLiveHits</class>     

        <properties>
                    <property name="hibernate.hbm2ddl.auto" value="update" />
            <!-- <property name= "hibernate.hbm2ddl.auto" value ="validate" /> create-drop -->
            <property name="hibernate.jdbc.fetch_size" value="15" />
            <property name="hibernate.jdbc.batch_size" value="10" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="true"></property>

            <!-- 
            <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>  
            <property name="javax.persistence.schema-generation-target" value="scripts"/>
            <property name="javax.persistence.ddl-create-script-target" value="e:\createSeats.sql"/>
            <property name="javax.persistence.ddl-drop-script-target" value="e:/dropSeats.sql"/>
             -->
        </properties>
    </persistence-unit>

</persistence>      

mysql driver的配置參考以前文檔,在

<driver name="mysql" module="com.mysqldatabase.mysql">      
調用:      
public class PlayDurationTask {

    @PersistenceContext(unitName = "statLog")
    private EntityManager emStatLog;

        String query = "SELECT *  FROM statistics.log_2016  ;";
        List aa = emStatLog.createNativeQuery(query).getResultList();
//ddl語句用 createNativeUpdate
        // 處理


        for (int i = 0; i < aa.size(); i++) {
            Object[] obj = (Object[]) aa.get(i);
            // 使用obj[0],obj[1],obj[2]...取出屬性 
            ... ...
        }
}      
class two{      

@PersistenceContext(unitName = "primary")

private EntityManager em ;

public void genLiveReport() {

em.createNativeQuery(LIVE_PLAY_USERS_BY_MAINID).executeUpdate();

logger.info(LIVE_PLAY_USERS_BY_MAINID);

}

參考:

Demystifying Datasource JTA and XA settings on JBoss-WildFly

One topic which is often misunderstood by middleware administrators is the configuration of JTA and XA attributesand their effect on transactions. Let's see more in practice.

Basically on a JBoss AS 6/WildFly configuration you can choose three different strategies as far as transactioons are concerned:

1) Setting jta = false and Non-XA datasource

1

​<​

​​

​datasource​

​ ​

​jta​

​=​

​"false"​

​  ​

​. . . >​

When setting this option you will be responsible for managing by yourself the transactions.  For example, the following code will work when using a Datasource with JTA=false:

2

3

4

5

6

7

8

9

10

11

12

​@Resource​

​(mappedName=​

​"java:jboss/datasources/ExampleDS"​

​)  ​

​private​

​DataSource ds;  ​

​. . . . . . . . . . .​

​Connection conn = ds.getConnection();​

​conn.setAutoCommit(​

​false​

​);​

​PreparedStatement stmt = conn.prepareStatement(​

​"INSERT into PROPERTY values (?,?)"​

​);​

​stmt.setString(​

​1​

​,key);​

​stmt.setString(​

​2​

​,value);​

​stmt.execute();​

​conn.commit();​

 Please note: the datasource with jta=false corresponds exactly to the older definition of local-tx-datasource you can find in JBoss AS 4/5

What if you are using JPA instead ?

So, when using plain JDBC you can handle by yourself transaction boundaries using commit/rollback. What about if you are using JPA with JTA=false? in short, the transaction-type for JPA will be RESOURCE_LOCAL. This would use basic JDBC-level transactions. In such scenario you are responsible for EntityManager (PersistenceContext/Cache) creating and tracking and you have to follow these rules:

  • You must use the EntityManagerFactory to get an EntityManager
  • The resulting EntityManager instance is a PersistenceContext/Cache An EntityManagerFactory can be injected via the @PersistenceUnit annotation only (not @PersistenceContext)
  • You are not allowed to use @PersistenceContext to refer to a unit of type RESOURCE_LOCAL
  • You must use the EntityTransaction API to begin/commit around every call to your EntityManger

Here is an example of using JPA with a RESOURCE_LOCAL transaction:

​@PersistenceUnit​

​(unitName=​

​"unit01"​

​)​

​private​

​EntityManagerFactory emf;​

​EntityManager em = emf.createEntityManager();​

​em.getTransaction().begin();​

​em.persist(mag);​

​em.getTransaction().commit();​

 2) Setting jta = true and Non-XA datasource

​<​

​datasource​

​jta​

​=​

​"true"​

​. . . >​

This is the default. When JTA is true, the JCA connection pool manager knows to enlist the connection into the JTA transaction. This means that, if the Driver and the database support it, you can use JTA transaction for a single resource.

​@PersistenceContext​

​(unitName = ​

​"unit01"​

​)​

​private​

​EntityManager entityManager;​

​public​

​void​

​addMovie(Movie movie) ​

​throws​

​Exception {​

​entityManager.persist(movie);​

​}​

If you try to manage JDBC transactions by yourself when jta=true an exception will be raised:

​12:11:17,145 SEVERE [com.sample.Bean] (http-/127.0.0.1:8080-1) null: java.sql.SQLException: You cannot set autocommit during a managed transaction!​

​at org.jboss.jca.adapters.jdbc.BaseWrapperManagedConnection.setJdbcAutoCommit(BaseWrapperManagedConnection.java:961)​

​at org.jboss.jca.adapters.jdbc.WrappedConnection.setAutoCommit(WrappedConnection.java:716)​

 3) Using an XA datasource

An XA transaction, in the most general terms, is a "global transaction" that may span multiple resources. A non-XA transaction always involves just one resource. 

​<​

​xa-datasource​

​. . . .>​

An XA transaction involves a coordinating transaction manager, with one or more databases (or other resources, like JMS) all involved in a single global transaction. Non-XA transactions have no transaction coordinator, and a single resource is doing all its transaction work itself.

The Transaction Manager coordinates all of this through a protocol called Two Phase Commit (2PC). This protocol also has to be supported by the individual resources. 

In terms of datasources, an XA datasource is a data source that can participate in an XA global transaction. A non-XA datasource can't participate in a global transaction