Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support default methods on mapper interfaces #709

Closed
BrassyPanache opened this issue Jun 16, 2016 · 10 comments
Closed

Support default methods on mapper interfaces #709

BrassyPanache opened this issue Jun 16, 2016 · 10 comments
Assignees
Labels
enhancement Improve a feature or add a new feature
Milestone

Comments

@BrassyPanache
Copy link

I'd like support for default methods on mapper interfaces. Currently if a method is not implemented (even if it is a default method) an exception is thrown:

      if (ms == null) {
        if(method.getAnnotation(Flush.class) != null){
          name = null;
          type = SqlCommandType.FLUSH;
        } else {
          throw new BindingException("Invalid bound statement (not found): " + statementName);
        }
      }

I believe it would be relatively easy to add a type SqlCommandType.ABSTRACT and call the underlying default function.

It is a useful feature because it allows you to put helper methods on mappers.

@harawata
Copy link
Member

I have just tested, but adding a default method to a mapper interface does not break a test case.
Please create a test case.

@cheuk-fung
Copy link

@harawata Here's a simple example mapper against the latest release (3.4.1):

package example;

import org.apache.ibatis.annotations.Select;

public interface ExampleMapper {
    @Select("SELECT 1")
    int select();

    default int defaultSelect() {
        return select();
    }
}

Calling select() works fine but calling defaultSelect() leads to a BindingException:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): example.ExampleMapper.defaultSelect
        at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:223)
        at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:48)
        at org.apache.ibatis.binding.MapperProxy.cachedMapperMethod(MapperProxy.java:59)
        at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:52)
        at com.sun.proxy.$Proxy32.defaultSelect(Unknown Source)
        ...

@kazuki43zoo
Copy link
Member

I think supporting default method is good idea!!

@harawata harawata added the enhancement Improve a feature or add a new feature label Jul 6, 2016
@VaverDariush
Copy link

Definitely a good idea. Data transformation through default methods would be a good feature IMO.
Consider those simple examples:

List<MyObject> getByIds(List<Long> ids);

default MyObject getById(Long id) {
  List<MyObject> list = getByIds(Arrays.asList(id));
  // checks
  return list.get(0);
}


List<MyObject> getAll();

default Map<Long, MyObject> getAllAsMap() {
 return getAll().stream().collect(Collectors.toMap(MyObject::getId, Functions.identity()));
}

... and so on

@harawata
Copy link
Member

harawata commented Jul 7, 2016

I wrote a patch.
But...it requires Java 7 and won't get merged until Java 9 is released.

Still, it would be great if you could test it and see if it works as you expect.
And feel free to send a PR if you found a way to achieve this in Java 6.

Cheers,
Iwao

@VaverDariush
Copy link

VaverDariush commented Jul 7, 2016

Default methods are Java 8. There are no default methods in Java 6 or 7. So there is no way to achieve this pre Java 8.

@harawata harawata added this to the 3.4.2 milestone Jul 10, 2016
@harawata harawata self-assigned this Jul 10, 2016
@harawata
Copy link
Member

Never mind. It should be fixed in the latest 3.4.2-SNAPSHOT.
Please let me know if it works as you expected.

@mchiareli
Copy link

mchiareli commented Jan 25, 2017

This is not working if the interface has default (package) access modifier.

`
java.lang.reflect.UndeclaredThrowableException
at org.apache.ibatis.submitted.usesjava8.default_method.$Proxy8.defaultGetUser(Unknown Source)
at org.apache.ibatis.submitted.usesjava8.default_method.DefaultMethodTest.shouldInvokeDefaultMethod(DefaultMethodTest.java:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.IllegalAccessException: class is not public: org.apache.ibatis.submitted.usesjava8.default_method.Mapper.defaultGetUser(Object[])User/invokeSpecial, from org.apache.ibatis.submitted.usesjava8.default_method.Mapper/2
at java.lang.invoke.MemberName.makeAccessException(MemberName.java:850)
at java.lang.invoke.MethodHandles$Lookup.checkAccess(MethodHandles.java:1534)
at java.lang.invoke.MethodHandles$Lookup.checkMethod(MethodHandles.java:1472)
at java.lang.invoke.MethodHandles$Lookup.getDirectMethodCommon(MethodHandles.java:1621)
at java.lang.invoke.MethodHandles$Lookup.getDirectMethodNoSecurityManager(MethodHandles.java:1615)
at java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(MethodHandles.java:1234)
at org.apache.ibatis.binding.MapperProxy.invokeDefaultMethod(MapperProxy.java:81)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53)
... 30 more

`

I just changed the org.apache.ibatis.submitted.usesjava8.default_method.Mapper to package level, and the test fails.

@harawata
Copy link
Member

Hi @mchiareli ,
Could you create a new issue for that?
Thanks!

@mchiareli
Copy link

#905 here it is. thanks.

pulllock pushed a commit to pulllock/mybatis-3 that referenced this issue Oct 19, 2023
pulllock pushed a commit to pulllock/mybatis-3 that referenced this issue Oct 19, 2023
… is called, invoke the default method instead of looking for a bound mapper method.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Improve a feature or add a new feature
Projects
None yet
Development

No branches or pull requests

6 participants