/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jdbi.v3.jpa; import java.util.Comparator; import java.util.List; import java.util.Objects; import org.assertj.core.api.AbstractListAssert; import org.jdbi.v3.core.rule.H2DatabaseRule; import org.jdbi.v3.sqlobject.SqlObjectPlugin; import org.jdbi.v3.sqlobject.statement.SqlQuery; import org.jdbi.v3.sqlobject.statement.SqlUpdate; import org.jdbi.v3.sqlobject.config.RegisterRowMapperFactory; import org.junit.Rule; import org.junit.Test; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.MappedSuperclass; import static org.assertj.core.api.Assertions.assertThat; public class JpaTest { private static final String INSERT_BY_PROPERTY_NAME = "insert into something(id, name) values (:id, :name)"; private static final String SELECT_BY_PROPERTY_NAME = "select id, name from something"; public static final String INSERT_BY_ANNOTATION_NAME = "insert into something (id, name) values (:foo, :bar)"; public static final String SELECT_BY_ANNOTATION_NAME = "select id as foo, name as bar from something"; public static final String ID_ANNOTATION_NAME = "foo"; public static final String NAME_ANNOTATION_NAME = "bar"; @Rule public H2DatabaseRule dbRule = new H2DatabaseRule().withPlugin(new SqlObjectPlugin()); interface Thing { int getId(); String getName(); } @Entity static class EntityThing implements Thing { private int id; private String name; public EntityThing() {} public EntityThing(int id, String name) { setId(id); setName(name); } public int getId() { return id; } public String getName() { return name; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } } public interface EntityThingDao { @SqlUpdate(INSERT_BY_PROPERTY_NAME) void insert(@BindJpa EntityThing thing); @SqlQuery(SELECT_BY_PROPERTY_NAME) @RegisterRowMapperFactory(JpaMapperFactory.class) List<EntityThing> list(); } @Test public void testEntityNoColumnAnnotations() throws Exception { EntityThing brian = new EntityThing(1, "Brian"); EntityThing keith = new EntityThing(2, "Keith"); EntityThingDao dao = dbRule.getSharedHandle().attach(EntityThingDao.class); dao.insert(brian); dao.insert(keith); List<EntityThing> rs = dao.list(); assertThatThing(rs).containsOnlyOnce(brian, keith); } @Entity static class FieldThing implements Thing { @Column private int id; @Column private String name; public FieldThing() {} public FieldThing(int id, String name) { setId(id); setName(name); } public int getId() { return id; } public String getName() { return name; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } } public interface FieldThingDao { @SqlUpdate(INSERT_BY_PROPERTY_NAME) void insert(@BindJpa FieldThing thing); @SqlQuery(SELECT_BY_PROPERTY_NAME) @RegisterRowMapperFactory(JpaMapperFactory.class) List<FieldThing> list(); } @Test public void testField() throws Exception { FieldThing brian = new FieldThing(1, "Brian"); FieldThing keith = new FieldThing(2, "Keith"); FieldThingDao dao = dbRule.getSharedHandle().attach(FieldThingDao.class); dao.insert(brian); dao.insert(keith); List<FieldThing> rs = dao.list(); assertThatThing(rs).containsOnlyOnce(brian, keith); } @Entity static class NamedFieldThing implements Thing { @Column(name = "foo") private int id; @Column(name = "bar") private String name; public NamedFieldThing() {} public NamedFieldThing(int id, String name) { setId(id); setName(name); } public int getId() { return id; } public String getName() { return name; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } } public interface NamedFieldThingDao { @SqlUpdate(INSERT_BY_ANNOTATION_NAME) void insert(@BindJpa NamedFieldThing thing); @SqlQuery(SELECT_BY_ANNOTATION_NAME) @RegisterRowMapperFactory(JpaMapperFactory.class) List<NamedFieldThing> list(); } @Test public void testNamedField() throws Exception { NamedFieldThing brian = new NamedFieldThing(1, "Brian"); NamedFieldThing keith = new NamedFieldThing(2, "Keith"); NamedFieldThingDao dao = dbRule.getSharedHandle().attach(NamedFieldThingDao.class); dao.insert(brian); dao.insert(keith); List<NamedFieldThing> rs = dao.list(); assertThatThing(rs).containsOnlyOnce(brian, keith); } @Entity static class GetterThing implements Thing { private int id; private String name; public GetterThing() {} public GetterThing(int id, String name) { setId(id); setName(name); } @Column public int getId() { return id; } @Column public String getName() { return name; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } } public interface GetterThingDao { @SqlUpdate(INSERT_BY_PROPERTY_NAME) void insert(@BindJpa GetterThing thing); @SqlQuery(SELECT_BY_PROPERTY_NAME) @RegisterRowMapperFactory(JpaMapperFactory.class) List<GetterThing> list(); } @Test public void testGetter() throws Exception { GetterThing brian = new GetterThing(1, "Brian"); GetterThing keith = new GetterThing(2, "Keith"); GetterThingDao dao = dbRule.getSharedHandle().attach(GetterThingDao.class); dao.insert(brian); dao.insert(keith); List<GetterThing> rs = dao.list(); assertThatThing(rs).containsOnlyOnce(brian, keith); } @Entity static class NamedGetterThing implements Thing { private int id; private String name; public NamedGetterThing() {} public NamedGetterThing(int id, String name) { setId(id); setName(name); } @Column(name = "foo") public int getId() { return id; } @Column(name = "bar") public String getName() { return name; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } } public interface NamedGetterThingDao { @SqlUpdate(INSERT_BY_ANNOTATION_NAME) void insert(@BindJpa NamedGetterThing thing); @SqlQuery(SELECT_BY_ANNOTATION_NAME) @RegisterRowMapperFactory(JpaMapperFactory.class) List<NamedGetterThing> list(); } @Test public void testNamedGetter() throws Exception { NamedGetterThing brian = new NamedGetterThing(1, "Brian"); NamedGetterThing keith = new NamedGetterThing(2, "Keith"); NamedGetterThingDao dao = dbRule.getSharedHandle().attach(NamedGetterThingDao.class); dao.insert(brian); dao.insert(keith); List<NamedGetterThing> rs = dao.list(); assertThatThing(rs).containsOnlyOnce(brian, keith); } @Entity static class SetterThing implements Thing { private int id; private String name; public SetterThing() {} public SetterThing(int id, String name) { setId(id); setName(name); } public int getId() { return id; } public String getName() { return name; } @Column public void setId(int id) { this.id = id; } @Column public void setName(String name) { this.name = name; } } public interface SetterThingDao { @SqlUpdate(INSERT_BY_PROPERTY_NAME) void insert(@BindJpa SetterThing thing); @SqlQuery(SELECT_BY_PROPERTY_NAME) @RegisterRowMapperFactory(JpaMapperFactory.class) List<SetterThing> list(); } @Test public void testSetter() throws Exception { SetterThing brian = new SetterThing(1, "Brian"); SetterThing keith = new SetterThing(2, "Keith"); SetterThingDao dao = dbRule.getSharedHandle().attach(SetterThingDao.class); dao.insert(brian); dao.insert(keith); List<SetterThing> rs = dao.list(); assertThatThing(rs).containsOnlyOnce(brian, keith); } @Entity static class NamedSetterThing implements Thing { private int id; private String name; public NamedSetterThing() {} public NamedSetterThing(int id, String name) { setId(id); setName(name); } public int getId() { return id; } public String getName() { return name; } @Column(name = "foo") public void setId(int id) { this.id = id; } @Column(name = "bar") public void setName(String name) { this.name = name; } } public interface NamedSetterThingDao { @SqlUpdate(INSERT_BY_ANNOTATION_NAME) void insert(@BindJpa NamedSetterThing thing); @SqlQuery(SELECT_BY_ANNOTATION_NAME) @RegisterRowMapperFactory(JpaMapperFactory.class) List<NamedSetterThing> list(); } @Test public void testNamedSetter() throws Exception { NamedSetterThing brian = new NamedSetterThing(1, "Brian"); NamedSetterThing keith = new NamedSetterThing(2, "Keith"); NamedSetterThingDao dao = dbRule.getSharedHandle().attach(NamedSetterThingDao.class); dao.insert(brian); dao.insert(keith); List<NamedSetterThing> rs = dao.list(); assertThatThing(rs).containsOnlyOnce(brian, keith); } @MappedSuperclass static class MappedSuperclassThing { private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } } @Entity static class ExtendsMappedSuperclassThing extends MappedSuperclassThing implements Thing { public ExtendsMappedSuperclassThing() {} public ExtendsMappedSuperclassThing(int id, String name) { setId(id); setName(name); } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } public interface MappedSuperclassThingDao { @SqlUpdate(INSERT_BY_PROPERTY_NAME) void insert(@BindJpa ExtendsMappedSuperclassThing thing); @SqlQuery(SELECT_BY_PROPERTY_NAME) @RegisterRowMapperFactory(JpaMapperFactory.class) List<ExtendsMappedSuperclassThing> list(); } @Test public void testMappedSuperclass() throws Exception { ExtendsMappedSuperclassThing brian = new ExtendsMappedSuperclassThing(1, "Brian"); ExtendsMappedSuperclassThing keith = new ExtendsMappedSuperclassThing(2, "Keith"); MappedSuperclassThingDao dao = dbRule.getSharedHandle().attach(MappedSuperclassThingDao.class); dao.insert(brian); dao.insert(keith); List<ExtendsMappedSuperclassThing> rs = dao.list(); assertThatThing(rs).containsOnlyOnce(brian, keith); } @Entity static class AnnotationPriorityThing implements Thing { @Column(name = ID_ANNOTATION_NAME) private int id; private String name; public AnnotationPriorityThing() {} public AnnotationPriorityThing(int id, String name) { setId(id); setName(name); } @Column(name = "ignored") public int getId() { return id; } @Column(name = NAME_ANNOTATION_NAME) public String getName() { return name; } @Column(name = "ignored") public void setId(int id) { this.id = id; } @Column(name = "ignored") public void setName(String name) { this.name = name; } } public interface AnnotationPriorityThingDao { @SqlUpdate(INSERT_BY_ANNOTATION_NAME) void insert(@BindJpa AnnotationPriorityThing thing); @SqlQuery(SELECT_BY_ANNOTATION_NAME) @RegisterRowMapperFactory(JpaMapperFactory.class) List<AnnotationPriorityThing> list(); } @Test public void testAnnotationPriority() throws Exception { // fields before getters before setters AnnotationPriorityThing brian = new AnnotationPriorityThing(1, "Brian"); AnnotationPriorityThing keith = new AnnotationPriorityThing(2, "Keith"); AnnotationPriorityThingDao dao = dbRule.getSharedHandle().attach(AnnotationPriorityThingDao.class); dao.insert(brian); dao.insert(keith); List<AnnotationPriorityThing> rs = dao.list(); assertThatThing(rs).containsOnlyOnce(brian, keith); } public interface SuperfluousColumnDao { @SqlUpdate(INSERT_BY_PROPERTY_NAME) void insert(@BindJpa FieldThing thing); @SqlQuery("select id, name, 'Rob Schneider' as extra from something") @RegisterRowMapperFactory(JpaMapperFactory.class) List<FieldThing> list(); } @Test public void testMapWithSuperfluousColumn() { FieldThing brian = new FieldThing(1, "Brian"); FieldThing keith = new FieldThing(2, "Keith"); SuperfluousColumnDao dao = dbRule.getSharedHandle().attach(SuperfluousColumnDao.class); dao.insert(brian); dao.insert(keith); List<FieldThing> rs = dao.list(); assertThatThing(rs).containsOnlyOnce(brian, keith); } public interface MissingColumnDao { @SqlUpdate("insert into something(id) values (:id)") void insert(@BindJpa FieldThing thing); @SqlQuery("select id from something") @RegisterRowMapperFactory(JpaMapperFactory.class) List<FieldThing> list(); } @Test public void testMissingColumn() { FieldThing brian = new FieldThing(1, "Brian"); FieldThing keith = new FieldThing(2, "Keith"); MissingColumnDao dao = dbRule.getSharedHandle().attach(MissingColumnDao.class); dao.insert(brian); dao.insert(keith); List<FieldThing> rs = dao.list(); assertThatThing(rs).containsOnlyOnce(new FieldThing(1, null), new FieldThing(2, null)); } @MappedSuperclass static class OverriddenSuperclassThing implements Thing { @Column(name = "foo") private int id; @Column(name = "bar") private String name; public int getId() { return id; } public String getName() { return name; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } } @Entity static class OverridingSubclassThing extends OverriddenSuperclassThing { public OverridingSubclassThing() {} public OverridingSubclassThing(int id, String name) { setId(id); setName(name); } @Override @Column(name = "meow") public int getId() { return super.getId(); } } public interface OverridingSubclassThingDao { @SqlUpdate("insert into something(id, name) values (:meow, :bar)") void insert(@BindJpa OverridingSubclassThing thing); @SqlQuery("select id as meow, name as bar from something") @RegisterRowMapperFactory(JpaMapperFactory.class) List<OverridingSubclassThing> list(); } @Test public void subclassAnnotationOverridesSuperclass() { // Annotated takes precedence over no annotation, even if annotated in superclass // Annotated member in subclass takes precedence over annotated member in superclass OverridingSubclassThing brian = new OverridingSubclassThing(1, "Brian"); OverridingSubclassThing keith = new OverridingSubclassThing(2, "Keith"); OverridingSubclassThingDao dao = dbRule.getSharedHandle().attach(OverridingSubclassThingDao.class); dao.insert(brian); dao.insert(keith); List<OverridingSubclassThing> rs = dao.list(); assertThatThing(rs).containsOnlyOnce(brian, keith); } private static <T extends Thing> AbstractListAssert<?, ? extends List<? extends T>, T, ?> assertThatThing(List<T> rs) { return assertThat(rs).usingElementComparator((Comparator<T>) (left, right) -> { if (left.getId() == right.getId()) { return Objects.toString(left.getName(), "").compareTo(Objects.toString(right.getName(), "")); } return left.getId() < right.getId() ? -1 : 1; }); } }