lunedì 20 febbraio 2012

iBatis 3, JOIN e Result Map Annidate

Salve.
Come sempre quando devo approcciarmi ad una nuova tecnologia mi vengono i nervi perché quando le cose semplici non vanno mi sembra di essere tornato ai tempi delle bestemmie su Basic.

Oggi vediamo nel dettaglio come realizzare una classica query tra due tabelle joinate con relazione 1:1 e 1:N fatte però con iBatis 3 che introduce alcune novità.

IMP: La guida spiegherà dettagliatamente la scrittura delle query e dei Dao indipendenti dalla versione quindi alcune asserzioni erano già valide nella 2.

Pariamo dalla base. Diciamo che abbiamo due tabelle:

TABELLA DITTA
id_ditta
nome_ditta
id_proprietario

TABELLA PROPRIETARI
id_proprietario
nome_propetario

Come vedete iniziamo con qualcosa di estremamente semplice. Due tabelle con una relazione 1:1 tra la Ditta ed il suo proprietario.

iBATIS 3 semplifica molto alcune fasi dell'infrastruttura come andremo a vedere.
Scriviamo però prima i due POJO:

POJO

package com.mime.model;

public class Ditta {

    private String idDitta;
    private String nomeDitta;
    private Integer idProprietario;
    
    private Proprietario proprietario;
    
    public Proprietario getProprietario() {
        return proprietario;
    }
    public void setProprietario(Proprietario proprietario) {
        this.proprietario = proprietario;
    }
    
    public String getIdDitta() {
        return idDitta;
    }
    public void setIdDitta(String idDitta) {
        this.idDitta = idDitta;
    }
    public String getNomeDitta() {
        return nomeDitta;
    }
    public void setNomeDitta(String nomeDitta) {
        this.nomeDitta = nomeDitta;
    }
    public Integer getIdProprietario() {
        return idProprietario;
    }
    public void setIdProprietario(Integer idProprietario) {
        this.idProprietario = idProprietario;
    }
    
}


Come vedete il POJO è molto semplice. L'unica cosa di cui vorrei parlare è l'oggetto Proprietario messo all'interno. Questo verrà riempito con una istanza dell'oggetto derivato dalla JOIN che faremo.

IMP: Nel caso di relazioni 1:N, basta usare una struttura come LIST. Quindi se una ditta può avere più di un proprietario, la sezione diventerebbe:

private List<Proprietario> proprietari;

Per comodità non mettiamo quello del singolo proprietario ma ipotiziamo di chiamarlo Proprietario.java.

DAO

Andiamo a vedere ora il DAO che nella versione 3 di iBatis risulta estremamente semplice in quanto basterà creare una interfaccia con i prototipi e usare nelle mappe i medesimi nomi.


package com.mime.model;

public interface DittaDao {

    public Ditta loadById(String id);

    public void insert(Ditta ditta);

    public void update(Ditta ditta);

    public void delete(String id);
    
}

Come vedete abbiamo mappato le quattro operazioni fondamentali. A questo punto dobbiamo solo creare gli XML che faranno girare il tutto.

SqlMapConfig

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <typeAliases>
        <typeAlias alias="Ditta" type="com.mime.model.Ditta"/>
        <typeAlias alias="Proprietario" type="com.mime.model.Proprietario"/>
    </typeAliases>
    
    <environments default="development">
        <environment id="development">
          <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/javadapoco"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
       </environment>
    </environments>
    
    <mappers>
         <mapper resource="com/mime/model/Ditta.xml"/>
         <mapper resource="com/mime/model/Proprietario.xml"/>
    </mappers>

</configuration>

Questo sopra è il file di configurazione che dice a iBatis 3 quali sono le mappe e gli Alias da usare. Come potete vedere abbiamo mappato le classi JAVA a degli Alias ed in basso abbiamo richiamato le due mappe che useremo.

PROPRIETARIO.XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="it.cup2000.orsa.core.dao.ProprietarioDao">
    
    <sql id="selectProprietario">
        select 
            PROPRIETARIO.id_proprietario as idProprietario,
            PROPRIETARIO.nome_proprietario as nomeProprietario
        from PROPRIETARIO
    </sql>    
    
    <resultMap id="proprietario" type="Proprietario">
         <id property="idProprietario" column="id_proprietario" />
         <result property="nomeProprietario" column="nome_proprietario" />
    </resultMap>
 
    <insert id="insert" parameterType="Proprietario" useGeneratedKeys="true" keyProperty="id">
        <selectKey keyProperty="id_proprietario" resultType="long" order="BEFORE">
            SELECT PROPRIETARIO_SEQ.NEXTVAL FROM DUAL
        </selectKey>
        insert into PROPRIETARIO (id_proprietario,nome_proprietario) 
        values (#{idProprietario},#{nomeProprietario})
    </insert>        
    
    <insert id="update" parameterType="Proprietario">
        update PROPRIETARIO set nome_proprietario = #{nomeProprietario}
        where id_proprietario = #{idProprietario}
    </insert>            
 
    <delete id="delete" parameterType="integer">
        delete from PROPRIETARIO where id_proprietario=#{value}          
    </delete>                
    
    <select id="loadById" resultMap="azienda" parameterType="String">
        <include refid="selectAzienda"/>  where id_proprietario=#{value}    
    </select>                
    
</mapper>

L'XML di proprietario è molto semplice e si tratta semplicemente di implementare le quattro funzioni base. Da notare come la SELECT abbia degli ALIAS per farli combaciare con i nomi delle proprietà del POJO, gli stessi alias usati in fase di insert ed update.
IMP: Quando il parameterType è una classe, allora occorre usare il nome della proprietà. Se invece è un tipo primitivo, si usa VALUE (vedi delete).

Andiamo ora a vedere la mappa di DITTA che è più interessante.

DITTA.XML


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="it.cup2000.orsa.core.dao.DittaDao">
    
    <sql id="selectDitta">
        select 
            DITTA.id_ditta as idDitta,
            DITTA.nome_ditta as nomeDitta
            DITTA.id_proprietario as idProprietario
        from DITTA
    </sql>    
    
    <resultMap id="ditta" type="Ditta">
         <id property="idDitta" column="id_ditta" />
         <result property="nomeDitta" column="nome_ditta" />
          <association property="proprietario" column="idProprietario" 
                     javaType="Proprietario"
                     select="com.mime.ProprietarioDao.loadById" />
    </resultMap>
     
    <select id="loadById" resultMap="ditta" parameterType="String">
        <include refid="selectDitta"/>  where id_ditta=#{value}    
    </select>            
    
    [...]    
    
</mapper>

Nell'esempio sopra vediamo la mappa per la Ditta. Ci concentriamo solo sulla select dato che le altre operazioni sono analoghe a quelle viste per proprietario.

Rircordiamo che stiamo realizzando una relazione 1:1 quindi UN solo proprietario.
Come vediamo abbiamo creato un codice SQL per la SELECT nella quale non è esplicitata la JOIN con la tabella proprietari. Questo perché sarà iBatis a farla per noi.

Nella resultMap useremo infatti il tag ASSOCIATION che avverte il sistema che la proprietà proprietario (di tipo Proprietario) dentro il POJO Ditta, sarà riempita dal risultato della query richiamata dal DAO ed il cui parametro è dato dalla colonna idProprietario.

IMP: il valore nell'attributo column DEVE essere quello della colonna della tabella di partenza che fungerà da parametro della query di SELECT nella seconda. In pratica quello che mettereste nella sintassi ON se esplicitaste la JOIN. Tale nome di colonna DEVE essere uguale all'alias. Nel nostro caso infatti non ho messo id_proprietario ma idProprietario dato che nel codice della SELECT avevo usato un alias.

Se la relazione è 1:N invece, la sintassi è molto simile anche se cambia la logica di fondo. Nel caso di relazione 1:N infatti, la colonna id_proprietario non avrebbe senso dentro la tabella DITTA ma viceversa ci sarebbe una colonna id_ditta dentro la tabella PROPRIETARIO.

Al posto di ASSOCIATION si userà quindi COLLECTION e si aggiungerà un ulteriore attributo come indicato:

<collection property="proprietario" column="idDitta" 
                     javaType="List" ofType="Proprietario"
                     select="com.mime.ProprietarioDao.loadByIdDitta" />

In pratica il JAVATYPE cambia ovviamente nell'oggetto LIST come visto sopra e si aggiunge l'attributo ofType per indicare che tipo di oggetti saranno messi dentro la lista (in questo caso sempre Proprietario). Il parametro dentro l'attributo column non sarà più idProprietario ma l'id della ditta che sarà ovviamente il parametro da dare alla loadByIdDitta che si presuppone ritorni una List<Proprietario>.




Nessun commento:

Posta un commento