package com.github.geequery.springdata;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.persistence.OptimisticLockException;
import jef.database.ORMConfig;
import jef.database.QB;
import jef.database.RecordsHolder;
import jef.database.query.Query;
import org.easyframe.enterprise.spring.CommonDao;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import com.github.geequery.springdata.repository.support.Update;
import com.github.geequery.springdata.test.entity.ComplexFoo;
import com.github.geequery.springdata.test.entity.Foo;
import com.github.geequery.springdata.test.entity.VersionLog;
import com.github.geequery.springdata.test.repo.ComplexFooDao;
import com.github.geequery.springdata.test.repo.FooDao;
import com.github.geequery.springdata.test.repo.FooEntityDao;
/**
* 与Spring集成的示例。 本示例使用的xml作为Spring配置。参见
* src/main/resources/spring/spring-test-case1.xml
*
* 4、MySQL下的时间精度造成版本无效 (修复)
* @author jiyi
*
*/
@ContextConfiguration(classes={com.github.geequery.springdata.config.PersistenceContext.class})
public class Case2 extends AbstractJUnit4SpringContextTests implements InitializingBean {
@javax.annotation.Resource
private CommonDao commonDao;
@javax.annotation.Resource
private FooDao foodao;
@javax.annotation.Resource
private FooEntityDao foodao2;
@javax.annotation.Resource
private ComplexFooDao complex;
@Test
public void testCase1() {
Foo foo = new Foo();
foo.setAge(1);
foo.setName("咋呼呼");
foodao.save(foo);
}
/**
* 使用方法名来定义查询的案例.
*
* @throws SQLException
*/
@Test
public void testFooDAO() throws SQLException {
// =============== 单字段查找 ==========
{
System.out.println("=== FindByName ===");
Foo foo = foodao.findByName("张三");
System.out.println(foo.getName());
System.out.println(foo.getId());
}
// =============查找,带排序============
{
System.out.println("=== FindByAgeOrderById ===");
List<Foo> fooList = foodao.findByAgeOrderById(0);
System.out.println(fooList);
int id = fooList.get(0).getId();
// =============== 使用悲观锁更新 ==================
boolean updated = foodao.lockItAndUpdate(id, new Update<Foo>() {
@Override
public void setValue(Foo value) {
value.setName("李四");
value.setRemark("悲观锁定");
}
});
if (updated) {
Foo u = foodao.findOne(id);
Assert.assertEquals("悲观锁定", u.getRemark());
}
}
// ==============使用分页,固定排序===========
{
System.out.println("=== FindByAge Page ===");
Page<Foo> fooPage = foodao.findByAgeOrderById(0, new PageRequest(1, 4));
System.out.println(fooPage.getTotalElements());
System.out.println(Arrays.toString(fooPage.getContent().toArray()));
}
// ==============分页+传入排序参数===========
{
System.out.println("=== FindAll(page+sort) ===");
Page<Foo> p = foodao.findAll(new PageRequest(0, 3, new Sort(new Order(Direction.DESC, "age"))));
System.out.println(p.getTotalElements());
System.out.println(Arrays.toString(p.getContent().toArray()));
}
// ===================不分页,传入排序参数===========================
{
System.out.println("=== FindAll(sort) ===");
Iterable<Foo> iters = foodao.findAll(new Sort(new Order(Direction.DESC, "id")));
System.out.println("list=" + iters);
}
// ===========查询多个ID的记录==============
{
System.out.println("=== FindAll(?,?,?) ===");
List<Integer> id = Arrays.<Integer> asList(1, 3, 4, 5);
Iterable<Foo> foos = foodao.findAll(id);
System.out.println("list=" + foos);
}
{
// =========== 在方法中携带运算符 like ===========
System.out.println("=== countByNameLike ===");
System.out.println(foodao.countByNameLike("%四"));
System.out.println("=== findByNameLike ===");
List<Foo> foo = foodao.findByNameLike("%四");
System.out.println(foo);
}
{
// 在方法中携带运算符 contains,并加上另一个条件
// 如果入参顺序和方法名中的一致,可以不加注解
System.out.println("=== findByNameContainsAndAge ===");
List<Foo> foos = foodao.findByNameContainsAndAge("李", 0);
System.out.println(foos);
}
{
// 多参数顺序测试否有问题
System.out.println("=== findByNameStartsWithAndAge ===");
List<Foo> foos = foodao.findByNameStartsWithAndAge(0, "李");
System.out.println(foos);
}
{
// 删除全部
System.out.println("=== DeleteAll() ===");
foodao.deleteAll();
}
}
@Test
public void testAbc() {
{
/**
* 最基本的@Query查询,注意需要@Param来绑定参数
*/
Foo foo = foodao2.findBysName("张三");
System.out.println(foo);
}
}
@Test
public void testFooDao2() {
{
/**
* 最基本的@Query查询,注意需要@Param来绑定参数
*/
Foo foo = foodao2.findBysName("张三");
System.out.println(foo);
}
{
/**
* Like查询,不使用@Param来绑定
*/
Foo foo = foodao2.findByusername("张");
System.out.println(foo);
}
// =========================
{
/**
* 1 @Query(name='xxx')可以从预定义的命名查询中获得一个配置好的查询语句 2 如果使用 @Param 对应
* :name,那么方法的参数先后顺序可以随意修改。反之,如果是 ?1 ?2方式进行参数绑定,则方法参数顺序有要求。
*/
//
List<Foo> foos = foodao2.findBySql(new Date(), "李四");
System.out.println(foos);
}
// =========================
{
/**
* 没有设置@param参数时, ?1 ?2 来分别表示第一个和第二个参数。此时方法参数顺序有要求。
*/
List<Foo> foos = foodao2.findBySql2("李四", new Date());
System.out.println(foos);
}
{
/**
* 单纯的Like运算符不会在查询子条件 李四上增加通配符。因此需要自己传入通配符 %李%
*/
System.out.println("=== findBySql3() ====");
Foo foo = foodao2.findBySql3("李", 0);
System.out.println(foo);
}
{
/**
* 用?1 ?2绑定时,顺序要注意。 如果在SQL语句中指定LIKE的查询方式是 ‘匹配头部’,那么查询就能符合期望
*/
System.out.println("=== findBySql4() ====");
Foo foo = foodao2.findBySql4(0, "李");
System.out.println(foo);
}
{
/**
* 1、SQL语句查询支持分页功能 2、传入null可以表示忽略对应的查询条件吗(动态SQL)? 默认情况下是不可以的,但是使用
*
* @IgnoreIf()注解可以化不可能为可能
*/
Page<Foo> page = (Page<Foo>) foodao2.findBySql5(0, null, new PageRequest(1, 4));
System.out.println(page.getTotalElements());
System.out.println(page.getContent());
}
{
/**
* 1、like <$string$>的用法 2、顺序不能用Spring-data的方法传入并使用。 但是可以换一种方法
*/
System.out.println("=== findBySql6() ====");
List<Foo> result = foodao2.findBySql6(0, "张", new Sort(new Order(Direction.DESC, "id")));
System.out.println(result);
System.out.println("=== findBySql6-2() ====");
result = foodao2.findBySql62(0, "张", "id desc");
System.out.println(result);
}
{
/**
* 1、条件自动省略 2、如果条件中带有 % _等特殊符号,会自动转义
*/
Page<Foo> page = foodao2.findBySql7(0, "李%", new PageRequest(3, 5));
System.out.println(page.getContent());
page = foodao2.findBySql7(0, "", new PageRequest(3, 5));
System.out.println(page.getContent());
}
{
/**
* 使用SQL语句插入记录
*/
int ii = foodao2.insertInto("六河", 333, "测试", new Date());
System.out.println(ii);
ii = foodao2.insertInto2("狂四", 555, "测试", new Date());
System.out.println(ii);
}
{
/**
* 使用SQL语句来update
*/
int ii = foodao2.updateFooSetAgeByAgeAndId(new Date(), 12, 2);
System.out.println(ii);
}
}
/**
* 当有复合主键时
*/
@Test
public void testComplexId() {
// 测试复合主键的情况
{
ComplexFoo cf = new ComplexFoo(1, 2);
cf.setMessage("test224");
complex.save(cf);
cf = new ComplexFoo(2, 2);
cf.setMessage("1222234324");
complex.save(cf);
}
{
ComplexFoo cf = complex.findOne(new int[] { 1, 2 });
System.out.println(cf);
cf.setMessage("修改消息!");
complex.save(cf);
}
{
Iterable<ComplexFoo> list = complex.findAll(Arrays.asList(new int[] { 1, 2 }, new int[] { 2, 2 }));
for (ComplexFoo foo : list) {
System.out.println(foo);
}
}
{
complex.delete(new int[] { 1, 2 });
}
}
/**
* 锁案例1-1: 乐观锁
*/
@Test(expected = OptimisticLockException.class)
public void testVersionUpdateAndOptLock() {
int id;
{
VersionLog v = new VersionLog();
v.setName("叶问");
commonDao.insert(v);
id = v.getId();
}
{
VersionLog v = commonDao.load(VersionLog.class, id);
v.setName("叶问天");
commonDao.update(v);
}
{
VersionLog v = commonDao.load(VersionLog.class, id);
VersionLog v2 = commonDao.load(VersionLog.class, id);
v2.setName("抢先更新");
commonDao.update(v2);
try {
v.setName("啥,已经被人更新了,那我不是写不进去了!");
commonDao.update(v);
} catch (OptimisticLockException e) {
e.printStackTrace();
throw e;
}
}
}
/**
* 锁案例1-2:批量更新模式下的乐观锁
*/
@Test
public void testVersionAndOptLockInBatch() {
int id;
{
VersionLog v = new VersionLog();
v.setName("我的");
commonDao.insert(v);
id = v.getId();
}
{
VersionLog v1 = commonDao.load(VersionLog.class, id);
VersionLog v2 = commonDao.load(VersionLog.class, id - 3);
VersionLog v3 = commonDao.load(VersionLog.class, id - 2);
VersionLog v4 = commonDao.load(VersionLog.class, id - 1);
VersionLog v_ = commonDao.load(VersionLog.class, id - 2);
v_.setName("再次抢先更新");
commonDao.update(v_);
v1.setName("更新1");
v2.setName("更新2");
v3.setName("又被抢了,还能好好做朋友吗?");
v4.setName("更新4");
int count = commonDao.batchUpdate(Arrays.asList(v1, v2, v3, v4));
Assert.assertEquals(3, count); // 该条记录没有更新
v3 = commonDao.load(v3);
Assert.assertEquals("再次抢先更新", v3.getName()); // 该条记录没有更新
/**
* 在Batch模式下,乐观锁可以阻止覆盖他人的记录(无法写入),但是无法检测出是哪一组记录因为冲突造成无法写入。
* 因此只能确认不覆盖其他人的记录。
*
* 此外,非按主键更新的场合下,乐观锁也不能生效。因为非按主键更新时的更新请求并不能对应要数据库的的特定记录,无法检查
* 记录是否非修改。无法要求乐观锁进行干预。
*/
}
}
/**
* 锁案例2:悲观锁的使用
*/
@Test
public void testPessimisticLock() {
int id;
{
VersionLog v = new VersionLog();
v.setName("我");
commonDao.insert(v);
id = v.getId();
}
{
Query<VersionLog> query = QB.create(VersionLog.class).addCondition(QB.between(VersionLog.Field.id, id - 4, id));
RecordsHolder<VersionLog> records = commonDao.selectForUpdate(query);
try {
for (VersionLog version : records) {
version.setName("此时:" + version.getName());
// version.setModified(System.currentTimeMillis());
}
records.commit();
} finally {
records.close();
}
}
{
Query<VersionLog> query = QB.create(VersionLog.class).addCondition(QB.between(VersionLog.Field.id, id - 4, id));
List<VersionLog> records = commonDao.find(query);
for (VersionLog version : records) {
System.out.println(version.getName());
}
}
}
/**
* 锁案例3:特定场景下不需要加锁更新
*/
@Test
public void testUpdateWithoutLock() {
List<Foo> foos = commonDao.find(QB.create(Foo.class));
Foo foo = foos.get(0);
QB.fieldAdd(foo, Foo.Field.age, 100);
foo.setName("姓名也更新了");
commonDao.update(foo);
}
/**
* 使用自行实现的扩展方法
*/
@Test
public void testCustom() {
ComplexFoo cf = new ComplexFoo(1, 2);
complex.someCustomMethod(cf);
}
/**
* 在使用Spring-data的同时,传统的commondao/Session等方式操作依然可以正常使用
*
* @throws SQLException
*/
@Test
public void test1() throws SQLException {
commonDao.getNoTransactionSession().dropTable(Foo.class);
commonDao.getNoTransactionSession().createTable(Foo.class);
{
Foo foo = new Foo();
foo.setName("Hello!");
commonDao.batchInsert(Arrays.asList(foo));
}
{
Foo foo = new Foo();
foo.setAge(3);
foo.setName("Hello!");
// update MY_FOO set age=3 where name='Hello!'
commonDao.updateByProperty(foo, "name");
}
{
Foo foo = commonDao.loadByPrimaryKey(Foo.class, 1);
System.out.println(foo.getName());
}
{
// 根据ID删除
commonDao.removeByField(Foo.class, "id", 1);
}
}
@Override
public void afterPropertiesSet() throws Exception {
ORMConfig.getInstance().setDebugMode(false);
commonDao.getNoTransactionSession().dropTable(Foo.class, VersionLog.class);
commonDao.getNoTransactionSession().createTable(Foo.class, VersionLog.class);
{
List<Foo> list = new ArrayList<Foo>();
list.add(new Foo("张三"));
list.add(new Foo("李四"));
list.add(new Foo("王五"));
list.add(new Foo("张昕"));
list.add(new Foo("张鑫"));
list.add(new Foo("测试"));
list.add(new Foo("张三丰"));
list.add(new Foo("李元吉"));
list.add(new Foo("李渊"));
list.add(new Foo("李建成"));
list.add(new Foo("李世民"));
list.add(new Foo("赵日天"));
list.add(new Foo("叶良辰"));
list.add(new Foo("玛丽苏"));
list.add(new Foo("龙傲天"));
foodao.save(list);
}
{
VersionLog v1 = new VersionLog();
v1.setName("一见钟情");
VersionLog v2 = new VersionLog();
v2.setName("两两相依");
VersionLog v3 = new VersionLog();
v3.setName("三生三世");
VersionLog v4 = new VersionLog();
v4.setName("四海为家");
commonDao.batchInsert(Arrays.asList(v1, v2, v3, v4));
}
ORMConfig.getInstance().setDebugMode(true);
System.out.println("============= 下面案例正式开始 ===========");
}
}