Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy Loading using MyBatis 3 with Java

Tags:

java

sql

mybatis

I am using Mybatis (3.2.7 version) as an ORM framework for my JAVA project. As I'm from JPA background, I was keen to explore the LAZYLOADING supported by Mybatis. But I couldn't get across anything substantial.
(I am configuring MYBATIS using JAVA API and annotations solely for querying purpose)

As per the Mybatis documentation: 1. lazyLoadingEnabled: default value=TRUE

Globally enables or disables lazy loading. When enabled, all relations will be lazily loaded. This value can be superseded for an specific relation by using the fetchType attribute on it.

2. aggressiveLazyLoading : default value=TRUE

When enabled, an object with lazy loaded properties will be loaded entirely upon a call to any of the lazy properties. Otherwise, each property is loaded on demand.

Using the following attributes, I tried the following code:

a. JAVA Classes :

Feedback.java

public class Feedback implements Serializable {
private static final long serialVersionUID = 1L;

private int id;
private String message;

   /**
   * while loading Feedback, I want sender object to be lazily loaded
   */
private User sender;
private boolean seen;

// getters and setters
}

User.java

public class User implements Serializable, {
private static final long serialVersionUID = 1L;
private int id;
private String email;

// getters and setters
}

b. DB schema:

Feedback table

                Table "public.feedback"

  Column | Type      |    Modifiers                       
-------------+-----------+-------------------------------------------------------
 id          | integer   | PRIMARY KEY
 seen        | boolean   | not null
 sender_id   | integer   | FOREIGN KEY (sender_id) REFERENCES users(id)
 message     | text      | 

User Table:

                Table "public.users"

Column   | Type     |     Modifiers                      
-------------+----------+----------------------------------------------------
id          | integer  | PRIMARY KEY
email       | text     | 

c. Configuring MyBatis via JAVA API:

DataSource dataSource = new PGSimpleDataSource();
        ((PGSimpleDataSource) dataSource).setServerName("localhost");
        ((PGSimpleDataSource) dataSource).setDatabaseName(dbName);
        ((PGSimpleDataSource) dataSource).setPortNumber(5432);
        ((PGSimpleDataSource) dataSource).setUser(new UnixSystem().getUsername());
        ((PGSimpleDataSource) dataSource).setPassword("");

        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment(dbName, transactionFactory, dataSource);
        Configuration configuration = new Configuration(environment);
             configuration.addMapper(FeedbackMapper.class);

            //
             configuration.setAggressiveLazyLoading(false);
             sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

d. Querying DB and DB Queries in Feedbackmapper:

d.1 Code in Feedbackmapper:

@Select("SELECT f.id, f.message, f.seen, f.sender_id FROM feedback f WHERE f.id= #{feedbackId}")
@Results(value = { 
        @Result(property = "id", column = "id"),
        @Result(property = "sender", column = "sender_id", javaType = User.class, one = @One(select = "getUser", fetchType=FetchType.DEFAULT))
})
public Feedback getFeedback(@Param("feedbackId") int feedbackId);

@Select("SELECT id, email FROM users WHERE id=#{id}")
public User getUser(int id);

d.2: Code to invoke the queries in feedbackMapper

    // setup Mybatis session factory and config
    Feedback feedback =feedbackMapper.getFeedback(70000);
    System.out.println(feedback);

But still the "sender" object is populated upon querying the getFeedback(id). I expect the sender object shouldn't be populated immediately but only when I call getSender() on the fetched feedback object . Please help.

My recent Observations:

Mybatis team has indeed got it wrong in their documentation ie in documentation:

  1. lazyLoadingEnabled: default value=TRUE

  2. aggressiveLazyLoading : default value=TRUE

    But looking at their source code:

     protected boolean lazyLoadingEnabled = false;
     protected boolean aggressiveLazyLoading = true;
    

    **However that being corrected, the results are not affected and lazy loading isnt working :( **

like image 289
hemantvsn Avatar asked Jun 03 '14 11:06

hemantvsn


People also ask

What is lazy loading in Java?

Lazy loading is a technique that enhances the efficiency of the program. In this section, we will discuss lazy loading in detail. Virtual proxy is the memory saving process that promotes the postponement of object creation. Observe the following program. FileName: LazyLoadingExample.java

What order does MyBatis load properties in?

If a property exists in more than one of these places, MyBatis loads them in the following order: Properties specified in the body of the properties element are read first, Properties loaded from the classpath resource or url attributes of the properties element are read second, and override any duplicate properties already specified,

Can I use MyBatis with multiple environments?

Again though, this could have a severe impact on the behaviour of MyBatis, so use caution. MyBatis can be configured with multiple environments. This helps you to apply your SQL Maps to multiple databases for any number of reasons. For example, you might have a different configuration for your Development, Test and Production environments.

How do I find the methods of each class in MyBatis?

StatementHandler (prepare, parameterize, batch, update, query) The details of these classes methods can be discovered by looking at the full method signature of each, and the source code which is available with each MyBatis release.


1 Answers

I think I found a way to enable the lazyloading (though not cent-percent sure):

  • MyBatis documentation has following setting in configuration:

Setting : lazyLoadTriggerMethods

Description : Specifies which Object's methods trigger a lazy load

Valid Values : A method name list separated by commas

Default: equals,clone,hashCode,toString

  • As per the source code, this thing maps properly to what is given in documentation:

    public class Configuration {
    // other attributes + methods
    protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new         
    String[] { "equals", "clone", "hashCode", "toString" }));
    }
    
    • I have changed the Mybatis configuration to follows:

      TransactionFactory transactionFactory = new JdbcTransactionFactory();
      Environment environment = new Environment(dbName, transactionFactory,  dataSource);
      Configuration configuration = new Configuration(environment);
      
      /**
      *This is the cause why LAZY LOADING is working now
      */
        configuration.getLazyLoadTriggerMethods().clear();
      
        ///////////////////////////////////////////////////
        configuration.setLazyLoadingEnabled(true);
        configuration.setAggressiveLazyLoading(false);
      
    • The queries in Mapper (mostly unchanged):

      @Select("SELECT id, message, seen, sender_id 
      FROM feedback WHERE f.id= #{feedbackId}")
      @Results(value = { 
         @Result(property = "id", column = "id"),
         @Result(property = "sender", column = "sender_id", javaType = User.class, one = @One(select = "getUser"))
      
      // Set fetchType as DEFAULT or LAZY or don't set at all-- lazy loading takes place
      // Set fetchType as EAGER --sender Object is loaded immediately
      
      })
      public Feedback getFeedback(@Param("feedbackId") int feedbackId);
      
      @Select("SELECT id, email FROM users WHERE id=#{id}")
      public User getUser(int id);
      

- JAVA Code to invoke mapper

        FeedbackMapper mapper = sqlSession.getMapper(FeedbackMapper.class);
        Feedback feedback =mapper.getFeedback(69999);                   
        System.out.println("1. Feedback object before sender lazily load: \n"+ feedback);
        System.out.println("2. Sender loaded explicitly \n" +feedback.getSender());
        System.out.println("3. Feedback object after sender loading \n" + feedback);
  • Output of the CODE

1. Feedback object before sender lazily load:

{id : 69999,  message : message123, sender : null, seen : false}

2. Sender loaded explicitly 

{id : 65538 , email: [email protected]}

3. Feedback object after sender loading:

{id : 69999, message : message123, sender : {id : 65538, email : [email protected]},
 seen : false}

  • Though this works satisfactorily, upon doing

configuration.getLazyLoadTriggerMethods().clear();

However to lack of documentation in Mybatis, I'm not sure, whether this is associated with any drawbacks as such.

like image 147
hemantvsn Avatar answered Oct 04 '22 23:10

hemantvsn