diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..129c104 --- /dev/null +++ b/.gitignore @@ -0,0 +1,76 @@ +.idea +*.iml + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +### Eclipse template + +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/ +## File-based project format: +*.iws + +### Maven template +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +src/profiles/development +src/profiles/production +Будет удалено checks/ +Будет удалено src/test/jetty/ +Будет удалено src/test/resources/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1d96ed4 --- /dev/null +++ b/pom.xml @@ -0,0 +1,101 @@ + + + + 4.0.0 + + ru.simplex_software + wicket-springdata + 1.0-SNAPSHOT + + wicket-springdata + + http://www.example.com + + + UTF-8 + 1.8 + 1.8 + + + + + junit + junit + 4.11 + test + + + org.springframework.data + spring-data-commons + 2.0.6.RELEASE + + + org.apache.wicket + wicket-core + 8.0.0-M7 + + + org.apache.wicket + wicket-spring + 8.0.0-M7 + + + org.hibernate + hibernate-core + 5.2.17.Final + + + org.springframework + spring-context + 5.0.5.RELEASE + + + + + + + + maven-clean-plugin + 3.0.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + UTF-8 + true + true + + + -Xlint:unchecked + + + + + maven-surefire-plugin + 2.20.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + + diff --git a/src/main/java/ru/simplex_software/wicket_springdata/HibernateModel.java b/src/main/java/ru/simplex_software/wicket_springdata/HibernateModel.java new file mode 100644 index 0000000..a8b6759 --- /dev/null +++ b/src/main/java/ru/simplex_software/wicket_springdata/HibernateModel.java @@ -0,0 +1,160 @@ +package ru.simplex_software.wicket_springdata; + +import org.apache.wicket.injection.Injector; +import org.apache.wicket.model.LoadableDetachableModel; +import org.apache.wicket.spring.injection.annot.SpringBean; +import org.hibernate.Hibernate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Persistable; +import org.springframework.data.repository.CrudRepository; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Модель содержит: + * Persistable сущность в прикреплённом состоянии, + * id и класс Persistable сущности в откреплённом состоянии. + */ +public class HibernateModel> extends LoadableDetachableModel { + private static final Logger LOG = LoggerFactory.getLogger(HibernateModel.class); + + @SpringBean + private RepositoryFinder repositoryFinder; + + /** + * Первичный кдюч хранимой сущности + */ + private Serializable id; + + /** + * Класс хранимой сущности + */ + private Class tClass; + + public HibernateModel() { + init(); + } + + /** + * Конструктор. + * + * @param entity может быть NULL. + */ + public HibernateModel(T entity) { + super(entity); + init(); + setObject(entity); + } + + public void setObject(T entity) { + if (entity != null && entity.isNew()) { + throw new IllegalArgumentException("primary key can't be null"); + } + super.setObject(entity); + } + + /** + * Открепление сущности. + */ + public void onDetach() { + LOG.debug("onDetach start"); + + // Получаем Persistable сущность. + T entity = getObject(); + if (entity == null) { + return; + } + + // Получаем Id и класс Persistable сущности. + id = entity.getId(); + tClass = (Class) Hibernate.getClass(entity); + LOG.debug("onDetach end"); + } + + private void init() { + Injector.get().inject(this); + } + + @Override + protected T load() { + LOG.debug("load start"); + + if (isAttached()) { + return getObject(); + } + if (tClass == null) { + return null; + } + final CrudRepository repository = repositoryFinder.findRepository(tClass); + LOG.trace("repository={}", repository); + return (T) repository.findById(id).get(); + } + + /** + * Оборачивает в модели элементы списка. + */ + public static > List> wrapElements(List list) { + final ArrayList> models = new ArrayList<>(list.size()); + for (E e : list) { + models.add(new HibernateModel<>(e)); + } + return models; + } + + /** + * Оборачивает в модели элементы списка и возвращает итератор. + */ + public static > Iterator> wrapAndIterateElements(List list) { + final ArrayList> models = new ArrayList<>(list.size()); + for (E e : list) { + models.add(new HibernateModel<>(e)); + } + return models.iterator(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (id == null) { + onDetach(); + } + if (id == null) {//if no object set + if (o instanceof HibernateModel) { + Object obj = ((HibernateModel) o).getObject(); + return obj == null; + } + return false; + } + + if (o == null || getClass() != o.getClass()) return false; + + HibernateModel that = (HibernateModel) o; + + if (!id.equals(that.id)) return false; + return tClass.equals(that.tClass); + } + + @Override + public int hashCode() { + if (id == null) { + onDetach(); // Для установки Id + } + if (id == null) { // if no object set + return -1; + } else { + return id.hashCode(); + } + } + + public RepositoryFinder getRepositoryFinder() { + return repositoryFinder; + } + + public void setRepositoryFinder(RepositoryFinder repositoryFinder) { + this.repositoryFinder = repositoryFinder; + } +} diff --git a/src/main/java/ru/simplex_software/wicket_springdata/RepositoryFinder.java b/src/main/java/ru/simplex_software/wicket_springdata/RepositoryFinder.java new file mode 100644 index 0000000..7b7ce55 --- /dev/null +++ b/src/main/java/ru/simplex_software/wicket_springdata/RepositoryFinder.java @@ -0,0 +1,67 @@ +package ru.simplex_software.wicket_springdata; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.data.domain.Persistable; +import org.springframework.data.repository.CrudRepository; + +import java.io.Serializable; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Collection; + +/** + * Находит подкласс репозитория. + */ +public class RepositoryFinder, K extends Serializable> implements ApplicationContextAware { + private static final Logger LOG = LoggerFactory.getLogger(RepositoryFinder.class); + + private ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + /** + * Находит репизоторий по типу Persistable. + * + * @param tClass класс Persistable. + * @return первый CrudRepository для класса. + */ + public CrudRepository findRepository(Class tClass) { + final Collection repositoryCollection = applicationContext.getBeansOfType(CrudRepository.class).values(); + + for (CrudRepository repository : repositoryCollection) { + final Class[] interfaces = repository.getClass().getInterfaces(); + for (Class interfaze : interfaces) { + for (Type pt : interfaze.getGenericInterfaces()) { + if (!(pt instanceof ParameterizedType)) { + continue; + } + final Type[] actualTypeArguments = ((ParameterizedType) pt).getActualTypeArguments(); + if (Arrays.asList(actualTypeArguments).contains(tClass)) { + return repository; + } + } + } + } + LOG.warn("repository not found. PersistentEntity={}", tClass.getName()); + return null; + } + + /** + * Находит репизоторий по типу репозитория Persistable. + * + * @param tClass класс Persistable. + * @return CrudRepository для класса. + */ + public CrudRepository findRepositoryByClass(Class> tClass) { + final Collection> repositoryCollection = applicationContext.getBeansOfType(tClass).values(); + return repositoryCollection.iterator().next(); + } +} \ No newline at end of file