Search in this blog

Monday, July 13, 2015

MyBatis-Spring Summary for Production Applications

MyBatis is my favorite ORM, when I first tried to use it I got surprised because the configuration was a really easy task.

I use it in every app with a relational database, I combine it with Spring as a Dependency Injection framework.

Most of the configuration I will write is using Annotations, read more about MyBatis-Spring to get a better understanding.

The next beans are for the Spring XML file configuration:

    <!-- DataSource -->
    <bean id="dataSource"
          class="org.apache.tomcat.jdbc.pool.DataSource">
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="url" value="jdbc:postgresql://localhost:5432/dbname" />
        <property name="username" value="user" />
        <property name="password" value="password" />
        <property name="maxWait" value="15" />
        <property name="removeAbandonedTimeout" value="15" />
        <property name="defaultAutoCommit" value="false" />
    </bean>


You should define a dataSource, I use Tomcat Connection Pool, you can use the one you prefer.

Define the sqlSessionFactory:

    <!-- MyBatis config -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
    </bean>


Defina a mapper bean (the one with the methods and sql queries):

    <!-- MyBatis mappers -->
    <bean id="comercioMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="com.alex.mapper.SampleMapper" />
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>


Create the Interface for com.alex.SampleMapper:

package com.alex.mapper;

@Service
public interface SampleMapper {
 // the code later
}


Create a sample POJO:

public class Sample {
 private Integer id;
 private String name;
 private Integer age;
 // getters / setters
}


Assume you have a table like:

CREATE TABLE sample (
 id SERIAL NOT NULL PRIMARY KEY,
 name VARCHAR(50) NOT NULL,
 age INT NOT NULL
);


Declare methods to Add / Update / Delete:

    @Insert( "INSERT INTO sample (name, age) VALUES ( #{s.name), #{s.age) )" )
    @Options(useGeneratedKeys = true, keyProperty = "s.id")
    void add(@Param("s") Sample s);

    @Delete( "DELETE FROM sample WHERE id = #{s.id)" )
    void delete(@Param("s") Sample s);

    @Update( "UPDATE sample SET name = #{s.name}, age = #{s.age} WHERE id = #{s.id}" )
    void update(@Param("s") Sample s);

    @Select( "SELECT * FROM sample WHERE id = #{id}" )
    Sample find(@Param("id") Integer id);


The important part here is the "add" method which will try to insert a new row in a table with auto generated keys, if succeed, the generated key will be stored in the id field of the stored object.

This is really good, but what about is your the fields in your object have different names of the table in the database?

For example:

public class Sample {
 Integer id;
 String theName;
 Integer age;
}


You have to tell MyBatis for handling "theName" field as "name" column:

    @Select( "SELECT * FROM sample WHERE id = #{id}" )
    @Results({
        @Result(property = "theName", column = "name")
    })
    Sample find(@Param("id") Integer id);


What about reading complex objects?

public class Sample {
 Integer id;
 Data data,
}

public class Data {
 String name;
 Integer age;
}


Is the same, can you see it?

    @Select( "SELECT * FROM sample WHERE id = #{id}" )
    @Results({
        @Result(property = "data.name", column = "name"),
        @Result(property = "data.age", column = "age")
    })
    Sample find(@Param("id") Integer id);


Have you heard about TypeHandler? What about it?

The objects:

public class Sample {
 Integer id;
 Other other;
}

public class Other {
 Integer id;
}


The table:

CREATE TABLE sample (
 id INT NOT NULL PRIMARY KEY,
 other_id INT NOT NULL
)


The mapper:

    @Select( "SELECT * FROM sample WHERE id = #{id}" )
    @Results({
        @Result(
          property = "other.id", column = "other_id", typeHandler = OtherTypeHandler.class
        )
    })
    Sample find(@Param("id") Integer id);


Define the TypeHandler:

public class OtherTypeHandler extends BaseTypeHandler<Other> {
    @Override
    public Other getNullableResult(ResultSet rs, String colName) throws SQLException {
        Other other = new Other();
        other.setId(rs.getInt(colName));
        return other;
    }

    @Override
    public Other getNullableResult(ResultSet rs, int colNum) throws SQLException {
        Other other = new Other();
        other.setId(rs.getInt(colNum));
        return other;
    }

    @Override
    public Other getNullableResult(CallableStatement cs, int colNum) throws SQLException {
        Other other = new Other();
        other.setId(cs.getInt(colNum));
        return other;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Other t, JdbcType jt) throws SQLException {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
}


These are all the things I had needed using MyBatis, in the moment I needed I invest some time to find them, I put it here for using them if I forget them and hoping can be useful for you and save your time.

See you in the next post.

No comments:

Post a Comment

Leave me a comment