/*
* Copyright 2008-2016 the original author or authors.
*
* 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 com.github.geequery.springdata.repository.support;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import jef.common.wrapper.IntRange;
import jef.database.DbClient;
import jef.database.DbUtils;
import jef.database.Field;
import jef.database.IQueryableEntity;
import jef.database.PojoWrapper;
import jef.database.QB;
import jef.database.RecordHolder;
import jef.database.Session;
import jef.database.dialect.type.ColumnMapping;
import jef.database.jpa.JefEntityManager;
import jef.database.meta.EntityType;
import jef.database.meta.ITableMetadata;
import jef.database.meta.MetaHolder;
import jef.database.query.ConditionQuery;
import jef.database.query.PKQuery;
import jef.database.query.Query;
import jef.database.query.SqlExpression;
import jef.tools.ArrayUtils;
import jef.tools.Assert;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.EntityManagerProxy;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.github.geequery.springdata.repository.GeeQueryExecutor;
import com.github.geequery.springdata.repository.GqRepository;
/**
* Default implementation of the
* {@link org.springframework.data.repository.CrudRepository} interface. This
* will offer you a more sophisticated interface than the plain
* {@link EntityManager} .
*
* @author Jiyi
* @param <T>
* the type of the entity to handle
* @param <ID>
* the type of the entity's identifier
*/
@Repository
public class GqRepositoryImpl<T, ID extends Serializable> implements GqRepository<T, ID>, GeeQueryExecutor<T> {
private MetamodelInformation<T, ID> meta;
// 这是Spring的SharedEntityManager的代理,只可从中提取EMF,不可直接转换,因此这个EM上携带了基于线程的事务上下文
private EntityManagerProxy em;
private final Query<?> q_all;
public GqRepositoryImpl(MetamodelInformation<T, ID> meta, EntityManagerProxy emf) {
this.meta = meta;
this.em = emf;
q_all = QB.create(meta.getMetadata());
}
@Override
public Page<T> findAll(Pageable pageable) {
Session s = getSession();
try {
long total = s.count(q_all);
Query<?> q = this.q_all;
if (pageable.getSort() != null) {
q = QB.create(meta.getMetadata());
setSortToSpec(q, pageable.getSort());
}
List<T> result = s.select(q, toRange(pageable));
return new PageImpl<T>(result, pageable, total);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
public long count() {
Session s = getSession();
try {
long total = s.count(q_all);
return total;
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
public List<T> findByExample(T example,String... fields) {
Session s = getSession();
try {
return s.selectByExample(example, fields);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@SuppressWarnings("unchecked")
public List<T> find(T obj) {
if (obj == null)
return Collections.emptyList();
try {
if (obj instanceof IQueryableEntity) {
return (List<T>) getSession().select((IQueryableEntity) obj);
} else {
ITableMetadata meta = MetaHolder.getMeta(obj);
PojoWrapper pojo = meta.transfer(obj, true);
if (!pojo.hasQuery() && pojo.getUpdateValueMap().isEmpty()) {
pojo.getQuery().setAllRecordsCondition();
}
List<PojoWrapper> result = getSession().select(pojo);
return PojoWrapper.unwrapList(result);
}
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
public T load(T entity) {
return load(entity, true);
}
public T load(T entity, boolean unique) {
if (entity == null)
return null;
try {
return getSession().load(entity, unique);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@SuppressWarnings("unchecked")
@Override
public <S extends T> S findOne(Example<S> example) {
return (S) findOne(toQuery(example));
}
@SuppressWarnings("unchecked")
@Override
public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
return (Page<S>) findAll(toQuery(example), pageable);
}
@Override
public <S extends T> long count(Example<S> example) {
Session s = getSession();
try {
return s.count(toQuery(example));
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
public <S extends T> boolean exists(Example<S> example) {
Session s = getSession();
try {
return s.count(toQuery(example)) > 0;
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
public T findOne(ConditionQuery spec) {
Session s = getSession();
try {
return s.load(spec);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
public List<T> findAll(ConditionQuery spec) {
Session s = getSession();
try {
return s.select(spec, null);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
public Page<T> findAll(ConditionQuery spec, Pageable pageable) {
Session s = getSession();
try {
long count = s.countLong(spec);
setSortToSpec(spec, pageable.getSort());
List<T> result = s.select(spec, toRange(pageable));
return new PageImpl<T>(result, pageable, count);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
public List<T> findAll(ConditionQuery spec, Sort sort) {
Session s = getSession();
try {
setSortToSpec(spec, sort);
return s.selectAs(spec, meta.getJavaType());
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
public long count(ConditionQuery spec) {
Session s = getSession();
try {
return s.countLong(spec);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@SuppressWarnings("unchecked")
@Override
public List<T> findAll() {
Session s = getSession();
try {
return (List<T>) s.select(q_all);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@SuppressWarnings("unchecked")
@Override
public List<T> findAll(Sort sort) {
Session s = getSession();
try {
@SuppressWarnings("rawtypes")
Query q_all = QB.create(meta.getMetadata());
setSortToSpec(q_all, sort);
return s.select(q_all);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
public T getOne(ID id) {
if (id == null)
return null;
Session s = getSession();
try {
Serializable[] ids = toId(id);
return s.load(meta.getMetadata(), ids);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@SuppressWarnings("unchecked")
@Override
public <S extends T> List<S> findAll(Example<S> example) {
Session s = getSession();
try {
return (List<S>) s.select(toQuery(example));
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@SuppressWarnings("unchecked")
@Override
public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
Session s = getSession();
try {
Query<?> query = toQuery(example);
setSortToSpec(query, sort);
return (List<S>) s.select(query);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
public T findOne(ID id) {
return getOne(id);
}
@Override
public boolean exists(ID id) {
return getOne(id) != null;
}
@Override
public Iterable<T> findAll(Iterable<ID> ids) {
Session s = getSession();
try {
return s.batchLoad(meta.getMetadata(), asIdList(ids));
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
// /////////////////////////////以此为界,读方法在上,写方法在下///////////////////////////////////
@Override
@Transactional
public <S extends T> S save(S entity) {
return getEntityManager().merge(entity);
}
@Override
@Transactional
public void delete(T entity) {
Session s = getSession();
try {
s.delete(entity);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
@Transactional
public void delete(Iterable<? extends T> entities) {
Session s = getSession();
try {
for (T t : entities) {
s.delete(t);
}
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
@Transactional
public void deleteAll() {
Session s = getSession();
try {
s.delete(q_all);
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
@Transactional
public <S extends T> List<S> save(Iterable<S> entities) {
Session s = getSession();
try {
List<S> result = asList(entities);
s.batchInsert(result);
return result;
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
@Transactional
public void deleteInBatch(Iterable<T> entities) {
Session s = getSession();
try {
s.batchDelete(asList(entities));
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
@Transactional
public void deleteAllInBatch() {
DbClient db = getNoTransactionSession();
try {
db.truncate(meta.getMetadata());
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
@Override
@Transactional
public void delete(ID id) {
Session s = getSession();
try {
s.delete(meta.getMetadata(), toId(id));
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
// ///////////////////////////////////////////////////
private <S> List<S> asList(Iterable<S> entities) {
List<S> list = new ArrayList<S>();
for (Iterator<S> iter = entities.iterator(); iter.hasNext();) {
list.add(iter.next());
}
return list;
}
private List<Serializable> asIdList(Iterable<ID> ids) {
List<Serializable> list = new ArrayList<Serializable>();
if (this.meta.isComplexPK()) {
for (ID id : ids) {
list.add(toId(id));
}
} else {
for (ID id : ids) {
list.add(id);
}
}
return list;
}
@SuppressWarnings("unchecked")
private Serializable[] toId(ID id) {
Serializable[] ids;
if (id.getClass().isArray()) {
ids = ArrayUtils.toObject(id, Serializable.class);
} else if (Collection.class.isAssignableFrom(id.getClass())) {
Collection<? extends Serializable> cs = (Collection<? extends Serializable>) id;
ids = ((Collection<? extends Serializable>) id).toArray(new Serializable[cs.size()]);
} else {
ids = new Serializable[] { id };
}
return ids;
}
private void setSortToSpec(ConditionQuery spec, Sort sort) {
if (sort == null)
return;
for (Order order : sort) {
Field field;
ColumnMapping column = this.meta.getMetadata().findField(order.getProperty());
if (column == null) {
field = new SqlExpression(order.getProperty());
} else {
field = column.field();
}
spec.addOrderBy(order.isAscending(), field);
}
}
private <S extends T> Query<?> toQuery(Example<S> example) {
return QueryByExamplePredicateBuilder.getPredicate(meta.getMetadata(), example);
}
private Session getSession() {
EntityManagerFactory emf = em.getEntityManagerFactory();
EntityManager em = EntityManagerFactoryUtils.doGetTransactionalEntityManager(emf, null);
if (em == null) { // 当无事务时。Spring返回null
em = emf.createEntityManager(null, Collections.EMPTY_MAP);
}
if (em instanceof JefEntityManager) {
return ((JefEntityManager) em).getSession();
}
throw new IllegalArgumentException(em.getClass().getName());
}
private DbClient getNoTransactionSession() {
JefEntityManager jem = (JefEntityManager) em.getTargetEntityManager();
return jem.getDbClient();
}
private EntityManager getEntityManager() {
EntityManager jem = em.getTargetEntityManager();
return jem;
}
private IntRange toRange(Pageable pageable) {
return new IntRange(pageable.getOffset() + 1, pageable.getOffset() + pageable.getPageSize());
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
@Transactional
public boolean lockItAndUpdate(ID id, Update<T> update) {
Assert.notNull(update);
ITableMetadata meta = this.meta.getMetadata();
try {
if (meta.getType() == EntityType.POJO) {
PKQuery<PojoWrapper> query = new PKQuery<PojoWrapper>(meta, toId(id));
RecordHolder<PojoWrapper> result = getSession().loadForUpdate(query.getInstance());
if (result == null)
return false;
try {
PojoWrapper pojo = result.get();
update.setValue((T) pojo.get());
pojo.refresh();
return result.commit();
} finally {
result.close();
}
} else {
PKQuery query = new PKQuery(meta, toId(id));
RecordHolder<?> result = getSession().loadForUpdate(query.getInstance());
if (result == null)
return false;
try {
update.setValue((T) result.get());
return result.commit();
} finally {
result.close();
}
}
} catch (SQLException e) {
throw DbUtils.toRuntimeException(e);
}
}
}