web-dev-qa-db-fra.com

Insertion / mise à jour par lots MyBatis pour Oracle

J'ai récemment commencé à apprendre à utiliser myBatis. Je suis maintenant confronté à un tel scénario, je dois constamment chercher une nouvelle liste d'objets via WebService, puis pour cette liste, j'ai besoin d'insérer/mettre à jour chaque objet dans la table Oracle DB via myBatis.

La partie délicate est que je ne peux pas simplement faire une insertion par lots à chaque fois, car certains objets peuvent déjà exister dans la base de données, pour ces enregistrements, je dois en mettre à jour les champs au lieu d'une nouvelle insertion.

Ma solution actuelle pourrait être très stupide, en utilisant Java, construire la liste des objets à partir du service Web, parcourir chacun d'eux, faire une sélection myBatis, si ce n'est pas un null (existe déjà dans la base de données), puis faire une mise à jour myBatis; sinon, faites un insert myBatis pour ce nouvel objet.

La fonction est atteinte. Mais mon responsable technique dit qu'il est très peu efficace, car faire une boucle for en utilisant Java et insérer/mettre à jour un par un consommera beaucoup de ressources système. Il m'a conseillé de faire une insertion par lots en utilisant myBatis en passant une liste d'objets dans.

L'insertion de lots dans myBatis est simple, cependant, comme je n'insère pas uniquement (pour les enregistrements existants, je dois mettre à jour), je ne pense pas que l'insertion de lots soit appropriée ici. J'ai googlé pendant un certain temps pour cela et j'ai réalisé que je devrais peut-être utiliser "fusionner" au lieu de "insérer" (pour Oracle).

Les exemples que j'ai recherchés pour la fusion dans myBatis ne concernent qu'un seul objet, pas un lot. Ainsi, je veux savoir si des experts pourraient me proposer des exemples sur la façon de faire une fusion par lots dans MyBatis (La bonne façon d'écrire un Mapper)?

19
Kevin

Dans mon cas, il y a aussi le même scénario. J'ai utilisé for loop pour vérifier si cet enregistrement existe dans la base de données ou non, puis d'après cela, j'ai ajouté cet objet à deux listes d'array pour l'insérer ou le mettre à jour. Et puis utilisé le lot pour l'insertion et la mise à jour après pour la boucle pour que la liste.

voici ex. pour la mise à jour en fonction de la situation différente

1] c'est pour la mise à jour

<foreach collection="attendingUsrList" item="model"  separator=";">
    UPDATE parties SET attending_user_count = #{model.attending_count}
    WHERE  fb_party_id = #{model.eid}  
</foreach>

2] c'est pour insérer

<insert id="insertAccountabilityUsers" parameterType="AccountabilityUsersModel" useGeneratedKeys="false">
    INSERT INTO accountability_users 
        (
            accountability_user_id, accountability_id, to_username,
            record_status, created_by, created_at, updated_by, updated_at
        ) 
    VALUES
    <foreach collection="usersList" item="model" separator=","> 
        (           
            #{model.accountabilityUserId}, #{model.accountabilityId}, #{model.toUsername}, 
            'A', #{model.createdBy}, #{model.createdAt}, #{model.updatedBy}, #{model.updatedAt}     
        )
    </foreach>
</insert>

Dans la méthode dao, déclarer comme

void insertAccountabilityUsers(@Param("usersList") List<AccountabilityUsersModel> usersList);

Mise à jour

Voici mon code de session batch

public static synchronized SqlSession getSqlBatchSession() {
    ConnectionBuilderAction connection = new ConnectionBuilderAction();
    sf = connection.getConnection();
    SqlSession session = sf.openSession(ExecutorType.BATCH);
    return session;
}

SqlSession session = ConnectionBuilderAction.getSqlSession(); 

En fait, j'ai déjà donné un exemple complet ici pour cette question

25
Sameer Kazi

La réponse acceptée n'est pas la méthode recommandée pour gérer les opérations par lots. Il n'affiche pas les vraies instructions de lot car le mode exécuteur de lot doit être utilisé lors de l'ouverture d'une session. Voir ceci post dans lequel un contributeur de code a recommandé que la bonne façon de mettre à jour par lot (ou insérer) est d'ouvrir une session en mode batch et d'appeler à plusieurs reprises la mise à jour (ou insérer) pour un seul enregistrement.

Voici ce qui fonctionne pour moi:

public void updateRecords(final List<GisObject> objectsToUpdate) {
    final SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession(ExecutorType.BATCH);
    try {
        final GisObjectMapper mapper = sqlSession.getMapper(GisObjectMapper.class);
        for (final GisObject gisObject : objectsToUpdate) {
            mapper.updateRecord(gisObject);
        }
        sqlSession.commit();
    } finally {
        sqlSession.close();
    }
}

N'utilisez pas foreach dans votre mise à jour/insert et assurez-vous qu'il ne met à jour/insère qu'un seul enregistrement. Je rencontrais des erreurs Oracle insolubles en le faisant selon la réponse acceptée (caractère invalide, déclaration non terminée, etc.). Comme l'indique le message lié, la mise à jour (ou insertion) affichée dans la réponse acceptée n'est en fait qu'une déclaration sql géante.

31
rimsky