`

hibernate系列(五)Session接口方法

阅读更多
Session接口方法主要有save、persist、load、get、update、saveOrUpdat、merge、delete等,这里主要是对我看hibernate书籍的一个实践加总结。

首先是save()方法:
以之前的Customer和Order为例,看下类文件:

public class Customer {

	private Long id;
	private String name;
	private String email;
	private Timestamp registeredTime;
	private Set<Order> orders;
//略get、set方法
}

public class Order {
	private Long id;
	private String orderNumber;
	private Customer customer;
//略get、set方法
}

映射文件Customer.hbm.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
          "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
	<class name="com.ligang.domain.Customer" table="customer">
		<id name="id" column="id" type="long">
			<generator class="identity"/>
		</id>
		<property name="name" column="name" type="string"/>
		<property name="email" column="email" type="string"/>
		<property name="registeredTime" column="registeredTime" type="timestamp"/>
		<set name="orders" cascade="save-update" inverse="true">
			<key column="customer_id"/>
			<one-to-many class="com.ligang.domain.Order"/>
		</set>
	</class>
</hibernate-mapping>

save方法如下所示:
@Test
	public void testSave(){
		Session session=hibernateDao.getSession();
		Transaction tx=session.beginTransaction();
		
		Customer customer=new Customer();
		customer.setName("小明");
		customer.setEmail("917312290@qq.com");
		Timestamp t=new Timestamp(System.currentTimeMillis());
		customer.setRegisteredTime(t);
		
		session.save(customer);
		System.out.println(customer.getId());
		customer.setEmail("sdfvdf@qq.com");
		
		tx.commit();
		session.close();
	}

我们会看到如下的sql语句:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
73
Hibernate: update hibernate.customer set name=?, email=?, registeredTime=? where id=?

会有一个insert语句和一个update语句。这里的save方法执行时,并没有真正的去执行一条insert语句,而是仅仅从数据库中获取下一个id,并赋值给customer,获取当时customer信息的一个快照,计划执行一条insert语句,然后在事务提交时才会去真正执行该语句,在真正执行前,如果你向数据库中插入一条记录,该记录则会使用下一个id,即customer虽然未向数据库插入,但是已经占据一个id了。执行完该insert语句后会发现当前的customer和已经持久化的customer是不一致的,然后就需要执行一次update语句。

再来看下persist方法
该方法和save()方法的作用是一样的都是将一个临时对象转变为持久化对象。但是它和save的区别下面来介绍:

@Test
	public void testSave(){
		Session session=hibernateDao.getSession();
		Transaction tx=session.beginTransaction();
		
		Customer customer=new Customer();
		customer.setName("小明");
		customer.setEmail("917312290@qq.com");
		Timestamp t=new Timestamp(System.currentTimeMillis());
		customer.setRegisteredTime(t);
		
		session.persist(customer);
		System.out.println(customer.getId());
		customer.setEmail("sdfvdf@qq.com");
		customer.setName("萨菲您稍等");
		
		tx.commit();
		session.close();
	}

上述正常情况下和save是一样的,不同之处先来看下官方文档:


这里说明了两点:
第一:persist并不保证一定会给对象的id赋值,这一赋值可能在flush时才会去执行。而save则不同,persist返回void,而save方法是返回id的,即save方法必须从数据库中取出一个可用的id。
第二:persist在事务之外是不会计划执行insert的,而save方法则会计划执行insert的,同时会从数据库中取出一个可用id。
对于第一点,什么情况下persist会为对象的id赋值,我目前还不了解,不再说明。
对于第二点,可做如下实验:

@Test
	public void testSave(){
		Session session=hibernateDao.getSession();
		
		Customer customer=new Customer();
		customer.setName("小明");
		customer.setEmail("917312290@qq.com");
		Timestamp t=new Timestamp(System.currentTimeMillis());
		customer.setRegisteredTime(t);
		
		session.persist(customer);
		System.out.println(customer.getId());
		customer.setEmail("sdfvdf@qq.com");
		customer.setName("萨菲您稍等");
		
		session.close();
	}

此时并没有开启事务,打印的信息如下:
null

没有insert语句,同时没有去获取id。
而对于save()方法:

@Test
	public void testSave(){
		Session session=hibernateDao.getSession();
		
		Customer customer=new Customer();
		customer.setName("小明");
		customer.setEmail("917312290@qq.com");
		Timestamp t=new Timestamp(System.currentTimeMillis());
		customer.setRegisteredTime(t);
		
		session.save(customer);
		System.out.println(customer.getId());
		customer.setEmail("sdfvdf@qq.com");
		customer.setName("萨菲您稍等");
		
		session.close();
	}

也没有开启事务,打印信息如下:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
85

save方法在没有事务的情况下,仍然计划执行一条insert语句,同时从数据库中获取一个可用id,虽然最终没有insert,但是此id已被占用。

load和get方法
这个比较好理解,get方法始终返回一个真正对象,而load方法则需要根据lazy属性的true和false采用不同的加载策略,当为lazy=true时采用延迟加载的策略,即返回一个代理对象,内部是由javassist来实现代理的。

update方法:

@Test
	public void testUpdate(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer=new Customer();
		customer.setName("小明");
		customer.setEmail("917312290@qq.com");
		session1.save(customer);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		customer.setName("小红");
		session2.update(customer);
		customer.setEmail("917312290小红@qq.com");
		
		tx2.commit();
		session2.close();
	}

首先使用session1将一个临时对象转化为持久化对象,关闭session1,则该持久化对象变为游离对象(含有主键),通过session2将该游离对象更新为持久化对象。我们可以看到如下sql:

Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
Hibernate: update hibernate.customer set name=?, email=?, registeredTime=? where id=?

虽然我们有多次set来更新cunstomer的内容,但是最终只有一次update语句。update方法所做的内容是,计划执行一条update语句,但是此时的更新参数并没有确定,只是在事务提交时才会确认更新的参数。这里和save方法就不太一样,save方法计划执行一条insert语句,同时将此时的数据的参数也确定下来了,一旦后面再次更新参数就要执行update语句来更新(见上述save介绍)。

对于update还有一个内容就是,当游离对象属性都没发生改变时,调用update语句仍然会执行一条update语句,如下:

@Test
	public void testUpdate(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer=new Customer();
		customer.setName("小明");
		customer.setEmail("917312290@qq.com");
		session1.save(customer);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		session2.update(customer);
		
		tx2.commit();
		session2.close();
	}

Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
Hibernate: update hibernate.customer set name=?, email=?, registeredTime=? where id=?

如果想在游离对象没有任何属性更新时就不进行update更新则需要设置select-before-update="true",即在更新前先执行一次查询,通过对比查询出来的数据和现在的数据是否发生变化来决定是否进行update操作。如下设置映射文件:
<class name="com.ligang.domain.Customer" table="customer" lazy="true" select-before-update="false">

还是上述同样的程序,执行结果如下:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
Hibernate: select customer_.id, customer_.name as name2_0_, customer_.email as email3_0_, customer_.registeredTime as register4_0_ from hibernate.customer customer_ where customer_.id=?

可以看到先执行一条select语句,然后发现数据并没有发生变化,所以就没有执行update语句。如果数据经常发生变化,则不需要设置select-before-update="true",因为会多于执行一条select语句。

一个session不能拥有两个及以上id相同的对象,一旦拥有一个,然后想保存第二个时就会发生NonUniqueObjectException异常,如下:

@Test
	public void testUpdate(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer1=new Customer();
		customer1.setName("小明");
		customer1.setEmail("917312290@qq.com");
		session1.save(customer1);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		Customer customer2=(Customer)session2.get(Customer.class,customer1.getId());
		
		customer1.setName("小红");
		session2.update(customer1);
		
		tx2.commit();
		session2.close();
	}

在session1关闭时,customer1对象就变为游离对象,然后session2加载了id为customer1对象id的持久化对象作为customer2,此时customer1和customer2的id是一样的,同时customer2已作为session2的持久化对象,此时更改customer1,然后去保存customer1,此时就会抛出NonUniqueObjectException异常,如下:
org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [com.ligang.domain.Customer#98]
	at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:617)
	at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:301)
	at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:244)
	at org.hibernate.event.internal.DefaultUpdateEventListener.performSaveOrUpdate(DefaultUpdateEventListener.java:55)
	at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
	at org.hibernate.internal.SessionImpl.fireUpdate(SessionImpl.java:739)
	at org.hibernate.internal.SessionImpl.update(SessionImpl.java:731)
	at org.hibernate.internal.SessionImpl.update(SessionImpl.java:726)
	at com.ligang.test.dao.CustomerDaoTest.testUpdate(CustomerDaoTest.java:47)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)


saveOrUpdate方法:
此方法包含了save和update的功能,当前对象为临时对象时会调用save方法,当前对象是游离对象时调用update方法。它怎么判断当前对象是临时对象和游离对象呢?如果主键id为Integer,则可以判断主键id是否为null来判断,id为null则为临时对象,否则为游离对象。如果主键id为int类型,则需要设置unsaved-value来进行区分,如下:

<id name="id" column="id" type="long" unsaved-value="0">
      <generator class="identity"/>
</id>

当对象的id等于unsaved-value值时就为临时对象,否则为游离对象。

merge方法:
对于上述update方法抛出NonUniqueObjectException异常,如果我们想不抛出异常,并且去更新持久化对象,就要使用merge方法,但是merge方法并不局限于此。merger的处理流程如下:
对于merge(customer1):
(1)如果customer1为游离对象,则根据它的id到session缓存中取持久化对象,如果取到则计划执行一条update语句,测试如下:

@Test
	public void testMerge1(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer1=new Customer();
		customer1.setName("小明");
		customer1.setEmail("917312290@qq.com");
		session1.save(customer1);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		Customer customer2=(Customer)session2.get(Customer.class,customer1.getId());
		
		customer1.setName("小红");
		session2.merge(customer1);
		
		tx2.commit();
		session2.close();
	}

打印的sql如下:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
Hibernate: update hibernate.customer set name=?, email=?, registeredTime=? where id=?

(2)如果customer1为游离对象,则根据它的id到session缓存中取持久化对象,如果未取到则从数据库中查找出,如果查到则同样更新属性,计划执行一条update语句,此种情况比情况1多了一步向数据库中查询的操作,测试如下:
@Test
	public void testMerge2(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer1=new Customer();
		customer1.setName("小明");
		customer1.setEmail("917312290@qq.com");
		session1.save(customer1);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		customer1.setName("小红");
		session2.merge(customer1);
		
		tx2.commit();
		session2.close();
	}

打印的sql如下:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
Hibernate: update hibernate.customer set name=?, email=?, registeredTime=? where id=?

情况一的查询是我们主动触发的,主要是将数据加载到session2的缓存中,与本情况的查询是不一样的,本情况的查询是因为在缓存中没有对象的持久化对象才触发向数据库中查找。
(3)如果customer1为游离对象,则根据它的id到session缓存中取持久化对象,如果未取到则从数据库中查找出,如果从数据库中也未取到,则会新建一个customer对象,并把customer1的属性复制过去,然后保存该新建的对象,返回该对象的引用,测试如下:

@Test
	public void testMerge3(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer1=(Customer) session1.get(Customer.class,100L);
		session1.delete(customer1);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		customer1.setName("小红的妈妈");
		Customer customer2=(Customer) session2.merge(customer1);
		System.out.println(customer1==customer2);
		tx2.commit();
		session2.close();
	}

打印的sql如下:
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
Hibernate: delete from hibernate.customer where id=?
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
false

首先是session1从数据库中加载id为100L的数据,然后删除它,关闭session1,则此时customer1则变为游离对象,更改此游离对象,调用merge方法,session2首先从缓存中找id为100L的对象,没有则从数据库中找id为100L的数据,所以会有一条select语句,然后从数据库中也没找到,则session2创建一个新对象customer2,把customer1的属性值复制给customer2,然后insert customer2,所以会有一条insert语句,由于此时customer2是新建的,并不是直接使用的customer1,所以customer1==customer2是false。
(4)如果customer1是临时对象,则同情况三后半部分一样,直接新建一个新的customer对象,然后复制插入,返回新对象的引用,测试如下:

@Test
	public void testMerge4(){
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		Customer customer1=new Customer();
		customer1.setName("小红的妈妈");
		Customer customer2=(Customer) session2.merge(customer1);
		System.out.println(customer1==customer2);
		System.out.println(customer1.getId());
		
		tx2.commit();
		session2.close();
	}

打印的sql如下:
Hibernate: insert into hibernate.customer (name, email, registeredTime) values (?, ?, ?)
false
null

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
delete方法:
(1)首先是删除一个持久化对象,测试如下:

@Test
	public void testDelete1(){
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		
		Customer customer1=(Customer)session2.get(Customer.class,102L);
		session2.delete(customer1);
		System.out.println(customer1.getId());
		
		tx2.commit();
		session2.close();
		System.out.println(customer1.getId());
	}

删除了持久化对象customer1,则从数据库中将其删除,同时将其从session持久化缓存中移除,但是没有删除其id值,真正的删除是发生在session清理缓存的时候,打印的sql如下:
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
102
Hibernate: delete from hibernate.customer where id=?
102

若想在删除一个对象时同时删除其id属性的值,则需要设置hibernate的hibernate.cfg.xml配置文件的hibernate.use_identifier_rollback属性,将其设置为true。同样的测试例子打印的sql如下:
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
0
Hibernate: delete from hibernate.customer where id=?
0

这里有个疑问就是,为什么不把id置为null,而是赋值为0(Customer的id类型为Long类型)。
(2)删除一个游离对象,测试如下:

@Test
	public void testDelete2(){
		Session session1=hibernateDao.getSession();
		Transaction tx=session1.beginTransaction();
		
		Customer customer1=(Customer) session1.get(Customer.class,99L);
		tx.commit();
		session1.close();
		
		Session session2=hibernateDao.getSession();
		Transaction tx2=session2.beginTransaction();
		System.out.println(customer1.getId());
		session2.delete(customer1);
		System.out.println(customer1.getId());
		tx2.commit();
		session2.close();
		System.out.println(customer1.getId());
	}

打印的sql如下:
Hibernate: select customer0_.id as id1_0_0_, customer0_.name as name2_0_0_, customer0_.email as email3_0_0_, customer0_.registeredTime as register4_0_0_ from hibernate.customer customer0_ where customer0_.id=?
99
99
Hibernate: delete from hibernate.customer where id=?
99

customer1在session1关闭后成为一个游离对象,具有id值,因此和删除持久化对象的区别并不大,同理设置hibernate.use_identifier_rollback属性将id删除,不再实验。


若想转载请注明出处:  http://lgbolgger.iteye.com/blog/2125015
作者:iteye的乒乓狂魔
  • 大小: 10.2 KB
分享到:
评论

相关推荐

    java web技术开发大全(最全最新)

    4.6.3 HttpServletRequest接口中的Session方法 4.6.4 实例:通过Cookie跟踪Session 4.6.5 实例:通过重写uRL跟踪Session 4.7 Web开发的中文问题 4.7.1 Java的编码原理 4.7.2 实例:解决输出中文乱码问题 ...

    java web开发技术大全

    4.6.3 HttpServletRequest接口中的Session方法 4.6.4 实例:通过Cookie跟踪Session 4.6.5 实例:通过重写uRL跟踪Session 4.7 Web开发的中文问题 4.7.1 Java的编码原理 4.7.2 实例:解决输出中文乱码...

    java面试题

    答:多线程有两种实现方法,一种是继承Thread类或者实现Runnable接口。同步就是在方法返回类型后面加上synchronized。 c#中的委托,事件是不是委托? 答:委托就是将方法作为一个参数带入另一个方法叫做委托,事件...

    基于SSH框架的BBS论坛JavaEE项目源码

    4、提供用户接口、其他系统用户接口调用设置以及接口管理(可与jeecms系列软件无缝对接实现单点登录) 5、用户自定义字段 6、禁用ip、id发帖、回帖 7、注册成功自动登录 8、设置在线活跃度等级 9、手机模板方案...

    springmybatis

    其实还有更简单的方法,而且是更好的方法,使用合理描述参数和SQL语句返回值的接口(比如IUserOperation.class),这样现在就可以至此那个更简单,更安全的代码,没有容易发生的字符串文字和转换的错误.下面是详细...

    Spring面试题

    因为 org.springframework.beans.factory.BeanFactory 是一个简单接口,所以可以针对各种底层存储方法实现。最常用的 BeanFactory 定义是 XmlBeanFactory,它根据 XML 文件中的定义装入 bean,如清单 1 所示。 清单...

    java开源论坛jeebbs系统源码包

    4、提供用户接口、其他系统用户接口调用设置以及接口管理(可与jeecms系列软件无缝对接实现单点登录) 5、用户自定义字段 6、禁用ip、id发帖、回帖 7、注册成功自动登录 8、设置在线活跃度等级 9、手机模板方案...

    java开源包1

    GiftedMotion是一个很小的,免费而且易于使用图像互换格式动画是能够设计一个有趣的动画了一系列的数字图像。使用简便和直截了当,用户只需要加载的图片和调整帧您想要的,如位置,时间显示和处理方法前帧。 Java的...

    JAVA上百实例源码以及开源项目

    在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天...

    JAVA上百实例源码以及开源项目源代码

    在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天...

    java开源包11

    GiftedMotion是一个很小的,免费而且易于使用图像互换格式动画是能够设计一个有趣的动画了一系列的数字图像。使用简便和直截了当,用户只需要加载的图片和调整帧您想要的,如位置,时间显示和处理方法前帧。 Java的...

    java开源包2

    GiftedMotion是一个很小的,免费而且易于使用图像互换格式动画是能够设计一个有趣的动画了一系列的数字图像。使用简便和直截了当,用户只需要加载的图片和调整帧您想要的,如位置,时间显示和处理方法前帧。 Java的...

    java开源包3

    GiftedMotion是一个很小的,免费而且易于使用图像互换格式动画是能够设计一个有趣的动画了一系列的数字图像。使用简便和直截了当,用户只需要加载的图片和调整帧您想要的,如位置,时间显示和处理方法前帧。 Java的...

    java开源包6

    GiftedMotion是一个很小的,免费而且易于使用图像互换格式动画是能够设计一个有趣的动画了一系列的数字图像。使用简便和直截了当,用户只需要加载的图片和调整帧您想要的,如位置,时间显示和处理方法前帧。 Java的...

    java开源包5

    GiftedMotion是一个很小的,免费而且易于使用图像互换格式动画是能够设计一个有趣的动画了一系列的数字图像。使用简便和直截了当,用户只需要加载的图片和调整帧您想要的,如位置,时间显示和处理方法前帧。 Java的...

    java开源包10

    GiftedMotion是一个很小的,免费而且易于使用图像互换格式动画是能够设计一个有趣的动画了一系列的数字图像。使用简便和直截了当,用户只需要加载的图片和调整帧您想要的,如位置,时间显示和处理方法前帧。 Java的...

    java开源包4

    GiftedMotion是一个很小的,免费而且易于使用图像互换格式动画是能够设计一个有趣的动画了一系列的数字图像。使用简便和直截了当,用户只需要加载的图片和调整帧您想要的,如位置,时间显示和处理方法前帧。 Java的...

    java开源包8

    GiftedMotion是一个很小的,免费而且易于使用图像互换格式动画是能够设计一个有趣的动画了一系列的数字图像。使用简便和直截了当,用户只需要加载的图片和调整帧您想要的,如位置,时间显示和处理方法前帧。 Java的...

    java开源包7

    GiftedMotion是一个很小的,免费而且易于使用图像互换格式动画是能够设计一个有趣的动画了一系列的数字图像。使用简便和直截了当,用户只需要加载的图片和调整帧您想要的,如位置,时间显示和处理方法前帧。 Java的...

Global site tag (gtag.js) - Google Analytics