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.
I have just tested, but adding a default method to a mapper interface does not break a test case.
Please create a test case.
@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)
...
I think supporting default method is good idea!!
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
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
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.
Never mind. It should be fixed in the latest 3.4.2-SNAPSHOT.
Please let me know if it works as you expected.
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.
Hi @mchiareli ,
Could you create a new issue for that?
Thanks!
Most helpful comment
Never mind. It should be fixed in the latest 3.4.2-SNAPSHOT.
Please let me know if it works as you expected.