Search code examples
javamysqlmybatisjava-record

Return id from MyBatis @Insert for immutable object


I have an immutable object Card:

public record Card(int id, int deck_id, int count, String name) {}

And a mapper file with an insert statement:

@Mapper
public interface DeckMapper {
    @Insert(value = "INSERT INTO deck (name) VALUES (#{name})")
    public int addDeck(Deck deck);
}

And a table in a MySQL 8 database:

+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| name  | varchar(255) | YES  |     | NULL    |                |
| id    | int          | NO   | PRI | NULL    | auto_increment |
+-------+--------------+------+-----+---------+----------------+

I would like addDeck to return the id of the deck it creates. Normally, the way I would do this is either @Options(useGeneratedKeys=true), or @SelectKey, but both solutions attempt to modify the underlying deck record, rather than just returning the id. This causes an exception because the object is immutable and therefore doesn't have a setter for id.


Solution

  • We encountered the same problem. MyBatis could update the id of our immutable object, but we didn't want to mutate immutable objects. The caller of our method has no reason to expect the immutable object to be mutated. What we ended up doing was to use a wrapper class.

    public class Wrapper<T>
    {
      Wrapper(T data){
        this.data = data;
      }
    
      T data;
      long id;
      // getters and setters
    }
    

    In our db code we had a method that was something like this:

    public int insertAndReturnId<T>(string statement, T data)
    {
      var wrappedData = Wrapper(data);
      sqlSessionTemplate.insert(statement, wrappedData);
      return wrappedData.id
    }
    

    We then used useGeneratedKeys=true as normal and accessed the parameters of whatever we inserted by using wrapper.propertyA in the SQL. We did not use annotations though, but a similar solution should be possible.