ThreadLocal 的原理讲述 + 基于ThreadLocal实现MVC中的M层的事务控制

本文详细介绍了ThreadLocal的工作原理及其在解决MVC架构中事务控制问题的应用。通过一个用户转账的例子,展示了ThreadLocal如何确保每个线程拥有独立的Connection对象,以实现线程间的变量隔离,同时避免了多线程共享数据时的并发问题。文章还探讨了ThreadLocal的set、get和remove方法的实现,并提醒读者在使用完ThreadLocal后务必调用remove以防止内存泄漏。此外,文章提到了ThreadLocal与传统的同步机制synchronized的区别,以及它们在多线程编程中的不同角色。

ThreadLocal 的原理讲述 + 基于ThreadLocal实现MVC中的M层的事务控制

[toc]

每博一文案

 

tex

复制代码

生活不是努力了就可以变好的,喜欢做的事情也不是轻易就可以做的。以前总听别人说, 坚持就好了,努力就好了,都会好的,可是真的做起来压根就不是这样。这种时候要怎么办? 这种时候还能轻易地相信时间吗? 我总是一时间不知道怎么回答:直到今天我决定记录这些日子的生活时,直到我写完以上的文字时,我 脑海里才出现了一个清晰的答案。四个字:尽力而为。 我想这样的。世事无常,分道扬镳,生老病死,我们常常没法得偿所愿。 然而我们都必须尽力而为。 我觉得挺好的:把眼前的事情做好就行了,路都是走着走着才知道能走到哪里的。 越是焦虑,就越是要回到生活里去。因为身处迷雾中本就很难找到方向,能看见的也就 眼前的五米,那就五米五米地一步步走下去。 至于路能走成什么样,又能走去哪里...... 走着走着,就都知道了。 但或许其实终点到底是哪里也不是那么重要。 重要的是,我们走了很远的路,最终找到的人,是我们自己。 是哪个可以很好地应对挫折,应对痛苦,应对生活的变故的自己。 是那个依然前行,依然努力,依然能够为了小事而欣喜,为了善良而感动的自己。 是那个终于学会了珍惜的自己,是那个不再害怕平方的自己。 生活如河,自己就是自己的船。 ——————卢思浩《你也走了,很远的路吧》

1. ThreadLocal 给概述

ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程 ,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意:

  • 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
  • 既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题

ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

  • 这种变量在多线程环境下访问(通过get和set方法访问)时能保证各个线程的变量相对独立于其他线程内的变量
  • 在线程的生命周期内起作用,可以减少同一个线程内多个函数或组件之间一些公共变量传递的复杂度

总的来说,ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景

下图可以增强理解:

2. 抛砖引玉——>ThreadLocal

从上述一篇文章中:我们运用 **MVC的架构模式——> 实现了用户转账的功能:**🔜🔜🔜  MVC 三层架构案例详细讲解_ChinaRainbowSea的博客-优快云博客 但是其中存在,一个事务处理的问题。

如下事务控制的处理是在:M(Model 模型层/业务逻辑处理层) 的源码(注意: 对应事务上的控制一定是在 M层当中的),我们可以看到其中并没有进行一个事务上从处理。

 

java

复制代码

package com.RainbowSea.bank.mvc; /** * service 翻译为:业务。 * AccountService 专门处理Account业务的一个类 * 在该类中应该编写纯业务代码。(只专注域业务处理,不写别的,不和其他代码混合在一块) * 只希望专注业务,能够将业务完美实现,少量bug. * <p> * 业务类一般起名:XXXService,XXXBiz... */ public class AccountService { // 这里的方法起名,一定要体现出,你要处理的是什么业务: // 我们要提供一个能够实现转账的业务的方法(一个业务对应一个方法) // 比如:UserService StudentService OrderService // 处理Account 转账业务的增删改查的Dao private AccountDao accountDao = new AccountDao(); /** * 完成转账的业务逻辑 * * @param fromActno 转出账号 * @param toActno 转入账号 * @param money 转账金额 */ public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException { // 查询余额是否充足 Account fromAct = accountDao.selectByActno(fromActno); if (fromAct.getBalance() < money) { throw new MoneyNotEnoughException("对不起,余额不足"); } // 程序到这里说明余额充足 Account toAct = accountDao.selectByActno(toActno); // 修改金额,先从内存上修改,再从硬盘上修改 fromAct.setBalance(fromAct.getBalance() - money); toAct.setBalance(toAct.getBalance() + money); // 从硬盘数据库上修改 int count = accountDao.update(fromAct); count += accountDao.update(toAct); if(count != 2) { throw new AppException("账户转账异常,请联系管理员"); } } }

如下:如果我们没有进行事务处理控制存在一个什么样的问题:

假如:

用户 act001 ——> 转账给用户 act002 ,10000元

转账的过程中,突然用户 act001 网络出现了问题,转账失败了。

注意:这里转账失败了,用户act001的钱是不应该减少的,因为我们没有转账成功嘛

可是这里,并没有进行一个事务上的控制,导致的结果就是,我们转账失败的,但是用户 act001 的钱少 10000,用户act002 的钱却没有增加。其中 用户 act001 的 10000 元丢失在了,网络中,这是不可以的。用户会发飙的,钱转账失败了,钱还少了。这不是坑钱嘛。

如下测试:

我们执行转账操作:act002 转账给用户 act001 ,10000元,中途发生网络中断:

下面我们进行“事务的控制”:

在Java中要进行事务的控制就需要使用到 Connectino对象了。

 

java

复制代码

// 开启事务,不会自动提交数据给数据库 connection.setAutoCommit(false); connection.commit(); // 提交数据 connection.rollback(); // 事务的回滚

 

java

复制代码

package com.RainbowSea.bank.mvc; import com.RainbowSea.bank.utils.DBUtil; import java.sql.Connection; import java.sql.SQLException; /** * service 翻译为:业务。 * AccountService 专门处理Account业务的一个类 * 在该类中应该编写纯业务代码。(只专注域业务处理,不写别的,不和其他代码混合在一块) * 只希望专注业务,能够将业务完美实现,少量bug. * <p> * 业务类一般起名:XXXService,XXXBiz... */ public class AccountService { // 这里的方法起名,一定要体现出,你要处理的是什么业务: // 我们要提供一个能够实现转账的业务的方法(一个业务对应一个方法) // 比如:UserService StudentService OrderService // 处理Account 转账业务的增删改查的Dao private AccountDao accountDao = new AccountDao(); /** * 完成转账的业务逻辑 * * @param fromActno 转出账号 * @param toActno 转入账号 * @param money 转账金额 */ public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException { // 查询余额是否充足 Account fromAct = accountDao.selectByActno(fromActno); Connection connection = DBUtil.getConnection(); try { if (fromAct.getBalance() < money) { throw new MoneyNotEnoughException("对不起,余额不足"); } // 程序到这里说明余额充足 Account toAct = accountDao.selectByActno(toActno); // 修改金额,先从内存上修改,再从硬盘上修改 fromAct.setBalance(fromAct.getBalance() - money); toAct.setBalance(toAct.getBalance() + money); // 开启事务,不会自动提交数据给数据库 connection.setAutoCommit(false); // 从硬盘数据库上修改 int count = accountDao.update(fromAct); // null 引用异常,模拟转账过程中发生网络异常,转账失败 String s = null; s.toString(); count += accountDao.update(toAct); if(count != 2) { throw new AppException("账户转账异常,请联系管理员"); } // 程序走到这说明,没有问题,提交数据给数据库 connection.commit(); // 提交数据 } catch (SQLException e) { try { connection.rollback(); // 事务的回滚 } catch (SQLException ex) { throw new RuntimeException(ex); } throw new RuntimeException(e); } finally { DBUtil.close(connection,null,null); } } }

我们再次进行一个,转账看看是否,真的做到的事务的处理:也就是,转账失败的,act001用户的钱不会减少。

如果结果:为什么我们使用了Connection 对象进行了一个事务的控制,但是,还是会少钱???事务上并没有控制成功。

为什么会出现如上:情况,明明我们使用了 Connection 进行了一个事务上的控制,但是,却还是会少钱,就没有把事务真正控制上了。

解释:

这里你却是是将事务开启了,但是,你开启的事务是对于当前:AccountService类当中的transfer( )方法当中的Connetion 局部变量进行了一个事务上的控制。我们对数据库的修改是存在两个位置的:

其中这两个位置上的 accountDao.update() 方法中同样是存在了一个 Connection 对象的

简单的说就是:我们对数据的更新,需要通过:两个位置上的更新

  1. AccountService类当中的transfer( )方法当中的Connetion 局部变量进行了一个事务上的控制。
  2. AccountDao 类当中的update() 方法到当中的Connection 局部变量进行一个事务上的控制。
 
 

复制代码

存在一个问题就是:我们这里的service 层虽然进行了事务的控制,但是这里的使用的 Connection事务控制 的对象是不一致的,也就是说:我们Connection的事务控制对应不上,我们对数据库修改的操作上,就导致无法对 数据库进行事务控制。

操作同一个事务,但是存在两个Connection ,而且这两者之间的Connection 对象是不一致的,就会导致事务的控制失败。

因为你控制了一个Connection,但是还存在一个Connection ,没有对事务进行控制。

如何解决上述问题:

解决方法:

既然一个操作同一个事务,存在两个不同的Connection。

那我们就控制成:同一个事务,虽然存在两个Connection,但是它们的值是一样的,也就是同一个事务上的处理,一个Connection就够了。

怎么做到,共用一个Connection,我们可以通过传引用类型参数的方式:

如下修改:

测试:

优化:

虽然我们上述:通过传引用类型的参数,对Connetion 对象进行了共用的操作。

但是存在一个问题就是:我们每次对数据库操作,进行一个事务的处理,在 M(Model)层都要先创建一个Connection对象,并将该对象作为参数传送给 XXxDao,这样的操作,大大提高代码的耦合度。背离了 "高内聚,低耦合" 的思想。

有没有别的方法,将Connetion 存储起来,做到同一个线程当中获取到的Connection 都是同一个,不同的线程获取到的Connection是不同的。

有的,我们的ThreadLocal 就实现了这种方式。

同一个线程,我们知道在同一个线程当的 Thread 线程对象是一样的如下测试:

,既然同一个线程的Thread 是一样,那么我们可不可以,创建一个大Map ,将 Thread 作为 key ,其中Connection 作为value,所有需要同一个线程共用Connection对象的,都从这个 大Map当中获取。因为同一个线程的 Thread 都是一样的,而通过Thread 作为key ,获取到的Value(也就是 Connection )也就是一样的了。

3. ThreadLocal 的模拟编写

根据上述的讲述,我们这里来模拟编写一个 大Map,将 Thread 作为 key ,其中Connection 作为value。其中这种在Java中就叫做:ThreadLocal

首先,这里我们先演示没有使用:大Map的结果:

自定义的Connection 类

 

java

复制代码

package com.rainbowSea.testThreadLocal; public class MyConnection { }

 

java

复制代码

package com.rainbowSea.testThreadLocal; public class UserDao { public void insert(){ MyConnection myConnection = new MyConnection(); System.out.println("UserDao Connection : " + myConnection); Thread thread = Thread.currentThread(); System.out.println("UserDao Thread : " + thread); } }

 

java

复制代码

package com.rainbowSea.testThreadLocal; public class UserService { public UserDao userDao = new UserDao(); public void save() { MyConnection myConnection = new MyConnection(); System.out.println("UserService Connection :" + myConnection); Thread thread = Thread.currentThread(); System.out.println("UserService Thread : " + thread); userDao.insert(); } }

 

java

复制代码

package com.rainbowSea.testThreadLocal; public class Test { public static void main(String[] args) { MyConnection myConnection = new MyConnection(); System.out.println("Test Connection : " + myConnection); Thread thread = Thread.currentThread(); System.out.println("Test Thread : " + thread); UserService userService = new UserService(); userService.save(); } }

使用 "大Map处理:"

 

java

复制代码

package com.rainbowSea.testThreadLocal; import java.util.HashMap; import java.util.Map; public class MyThreadLocal<T> { /** * 所有需要和当前线程绑定的数据要放到整个容器当中 */ private Map<Thread,T> map = new HashMap<Thread,T>(); /** * 向ThreadLocal 中绑定数据 * 注意是:Thread.currentThread() 当前线程作为 key 存在 */ public void set(T t) { map.put(Thread.currentThread(),t); } /** * 向ThreadLocal 当中获取数据 * 注意:获取到的是当前线程的Connection绑定的数据, */ public T get() { // 通过 Thread.currentThread()当中线程作为key ,获取到对应的 value值 return map.get(Thread.currentThread()); } /** * 移除ThreadLocal当中数据 * 注意:移除的是Thread.currentThread()当前线程作为key 存储的数据信息。 */ public void remove() { map.remove(Thread.currentThread()); } }

 

java

复制代码

package com.rainbowSea.testThreadLocal; public class DBUtil { // 静态变量特点:类加载时执行,并且只执行一次 // 全局的大Map集合 public static MyThreadLocal<MyConnection> local = new MyThreadLocal<MyConnection>(); /** * 每一次都调用这个方法来获取Connection 对象 */ public static MyConnection getConnection() { // 从这个大的Map当中获取 Connection 对象 MyConnection connection = local.get(); // 如果是第一次:获取到的话这个 大MyThreadLocal 是没有存储到 Connection 对象的 // 所有我们需要向 MyThreadLocal 添加上 if (connection == null) { connection = new MyConnection(); // 添加到 这个大Map当中 local.set(connection); } // 返回从这个大Map当中获取到的Connection对象 return connection; } }

 

java

复制代码

package com.rainbowSea.testThreadLocal; public class Test { public static void main(String[] args) { // 从大Map MyThreadLocal中获取Connection对象 MyConnection myConnection = DBUtil.getConnection(); System.out.println("Test Connection : " + myConnection); UserService userService = new UserService(); userService.save(); } }

 

java

复制代码

package com.rainbowSea.testThreadLocal; public class UserService { public UserDao userDao = new UserDao(); public void save() { // 从大Map MyThreadLocal中获取Connection对象 MyConnection myConnection = DBUtil.getConnection(); System.out.println("UserService Connection :" + myConnection); userDao.insert(); } }

 

java

复制代码

package com.rainbowSea.testThreadLocal; public class UserDao { public void insert(){ // 从大Map MyThreadLocal中获取Connection对象 MyConnection myConnection = DBUtil.getConnection(); System.out.println("UserDao Connection : " + myConnection); } }

测试:

4. ThreadLocal 源码原理分析

下面我们来看看,Java为我们提供的 ThreadLocal 类吧

ThreadLocal的主要用途是实现线程间变量的隔离,表面上他们使用的是同一个ThreadLocal, 但是实际上使用的值value却是自己独有的一份。 用一图直接表示threadlocal 的使用方式

从图中我们可以当线程使用threadlocal 时,是将threadlocal当做当前线程thread的属性ThreadLocalMap 中的一个Entry的key值,实际上存放的变量是Entry的value值,我们实际要使用的值是value值。 value值为什么不存在并发问题呢,因为它只有一个线程能访问。threadlocal我们可以当做一个索引看待,可以有多个threadlocal 变量,不同的threadlocal对应于不同的value值,他们之间互不影响。ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。简单的说就是:实现一个线程当中的信息对象共用,共享。代替传引用类型参数的方式。

这里我们使用ThreadLocal是基于一个用户转账的案例来讲解的,为了解决事务上控制问题,一个线程共用一个Connection 。其中的我们的ThreadLocal 就作为了一个容器,其中的key 存储的就是当前线程,而value值则是对应Connection

5. ThreadLocal 常用方法

方法名描述
ThreadLocal()创建ThreadLocal对象
public void set( T value)设置当前线程绑定的局部变量
public T get()获取当前线程绑定的局部变量
public T remove()移除当前线程绑定的局部变量,该方法可以帮助JVM进行GC
protected T initialValue()返回当前线程局部变量的初始值

5.1 ThreadLocal的set()方法

 

java

复制代码

public void set(T value) { //1、获取当前线程 Thread t = Thread.currentThread(); //2、获取线程中的属性 threadLocalMap ,如果threadLocalMap 不为空, //则直接更新要保存的变量值,否则创建threadLocalMap,并赋值 ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else // 初始化thradLocalMap 并赋值 createMap(t, value); }

 

java

复制代码

/** * 设置当前线程对应的ThreadLocal的值 * @param value 将要保存在当前线程对应的ThreadLocal的值 */ public void set(T value) { // 获取当前线程对象 Thread t = Thread.currentThread(); // 获取此线程对象中维护的ThreadLocalMap对象 ThreadLocalMap map = getMap(t); // 判断map是否存在 if (map != null) // 存在则调用map.set设置此实体entry,this这里指调用此方法的ThreadLocal对象 map.set(this, value); else // 1)当前线程Thread 不存在ThreadLocalMap对象 // 2)则调用createMap进行ThreadLocalMap对象的初始化 // 3)并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中 createMap(t, value); } /** * 获取当前线程Thread对应维护的ThreadLocalMap * * @param t the current thread 当前线程 * @return the map 对应维护的ThreadLocalMap */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } /** *创建当前线程Thread对应维护的ThreadLocalMap * @param t 当前线程 * @param firstValue 存放到map中第一个entry的值 */ void createMap(Thread t, T firstValue) { //这里的this是调用此方法的threadLocal t.threadLocals = new ThreadLocalMap(this, firstValue); }

从上面的代码可以看出,ThreadLocal set赋值的时候首先会获取当前线程thread,并获取thread线程中的ThreadLocalMap属性。如果map属性不为空,则直接更新value值,如果map为空,则实例化threadLocalMap,并将value值初始化。

那么ThreadLocalMap又是什么呢,还有createMap又是怎么做的,我们继续往下看。大家最后自己再idea上跟下源码,会有更深的认识。

 

java

复制代码

static class ThreadLocalMap { /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } }

可看出ThreadLocalMap是ThreadLocal的内部静态类,而它的构成主要是用Entry来保存数据 ,而且还是继承的弱引用。在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value。详细内容要大家自己去跟。

 

java

复制代码

//这个是threadlocal 的内部方法 void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } //ThreadLocalMap 构造方法 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }

  1. 获取当前线程,并根据当前线程获取一个Map
  2. 如果获取的Map不为空,则将参数设置到Map中(当前ThreadLocal的引用作为key)
  3. 如果Map为空,则给该线程创建 Map,并设置初始值

5.2 ThreadLocal的get( )方法

 

java

复制代码

/** * 返回当前线程中保存ThreadLocal的值 * 如果当前线程没有此ThreadLocal变量, * 则它会通过调用{@link #initialValue} 方法进行初始化值 * @return 返回当前线程对应此ThreadLocal的值 */ public T get() { // 获取当前线程对象 Thread t = Thread.currentThread(); // 获取此线程对象中维护的ThreadLocalMap对象 ThreadLocalMap map = getMap(t); // 如果此map存在 if (map != null) { // 以当前的ThreadLocal 为 key,调用getEntry获取对应的存储实体e ThreadLocalMap.Entry e = map.getEntry(this); // 对e进行判空 if (e != null) { @SuppressWarnings("unchecked") // 获取存储实体 e 对应的 value值,即为我们想要的当前线程对应此ThreadLocal的值 T result = (T)e.value; return result; } } /* 初始化 : 有两种情况有执行当前代码 第一种情况: map不存在,表示此线程没有维护的ThreadLocalMap对象 第二种情况: map存在, 但是没有与当前ThreadLocal关联的entry */ return setInitialValue(); } /** * 初始化 * @return the initial value 初始化后的值 */ private T setInitialValue() { // 调用initialValue获取初始化的值 // 此方法可以被子类重写, 如果不重写默认返回null T value = initialValue(); // 获取当前线程对象 Thread t = Thread.currentThread(); // 获取此线程对象中维护的ThreadLocalMap对象 ThreadLocalMap map = getMap(t); // 判断map是否存在 if (map != null) // 存在则调用map.set设置此实体entry map.set(this, value); else // 1)当前线程Thread 不存在ThreadLocalMap对象 // 2)则调用createMap进行ThreadLocalMap对象的初始化 // 3)并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中 createMap(t, value); // 返回设置的值value return value; }

执行流程

  1. 获取当前线程, 根据当前线程获取一个Map
  2. 如果获取的Map不为空,则在Map中以ThreadLocal的引用作为key来在Map中获取对应的Entrye,否则转到4
  3. 如果e不为null,则返回e.value,否则转到4
  4. Map为空或者e为空,则通过initialValue函数获取初始值value,然后用ThreadLocal的引用和value作为firstKey和firstValue创建一个新的Map

5.3 ThreadLocal的remove( )方法

 

java

复制代码

/** * 删除当前线程中保存的ThreadLocal对应的实体entry */ public void remove() { // 获取当前线程对象中维护的ThreadLocalMap对象 ThreadLocalMap m = getMap(Thread.currentThread()); // 如果此map存在 if (m != null) // 存在则调用map.remove // 以当前ThreadLocal为key删除对应的实体entry m.remove(this); }

执行流程:

  1. 首先获取当前线程,并根据当前线程获取一个Map
  2. 如果获取的Map不为空,则移除当前ThreadLocal对象对应的entry

remove()方法,直接将ThrealLocal 对应的值从当前相差Thread中的ThreadLocalMap中删除。

为什么要删除,这涉及到内存泄露的问题?。

实际上 ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。

所以如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来 ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。但是,value 是强引用,不会被清理,这样一来就会出现 key 为 null 的 value。

ThreadLocal其实是与线程绑定的一个变量,如此就会出现一个问题:如果没有将ThreadLocal内的变量删除(remove)或替换,它的生命周期将会与线程共存。通常线程池中对线程管理都是采用线程复用的方法,在线程池中线程很难结束甚至于永远不会结束,这将意味着线程持续的时间将不可预测,甚至与JVM的生命周期一致。举个例字,如果ThreadLocal中直接或间接包装了集合类或复杂对象,每次在同一个ThreadLocal中取出对象后,再对内容做操作,那么内部的集合类和复杂对象所占用的空间可能会开始持续膨胀。

5.4 ThreadLocal 的 initialValue( )方法

  • 此方法的作用是返回该线程局部变量的初始值
  • 这个方法是一个延迟调用方法,从上面的代码我们得知,在set方法还未调用而先调用了get方法时才执行,并且仅执行1次
  • 这个方法缺省实现直接返回一个null
  • 如果想要一个除null之外的初始值,可以重写此方法。(备注: 该方法是一个protected的方法,显然是为了让子类覆盖而设计的)
 

java

复制代码

/** * 返回当前线程对应的ThreadLocal的初始值 * 此方法的第一次调用发生在,当线程通过get方法访问此线程的ThreadLocal值时 * 除非线程先调用了set方法,在这种情况下,initialValue 才不会被这个线程调用。 * 通常情况下,每个线程最多调用一次这个方法。 * * <p>这个方法仅仅简单的返回null {@code null}; * 如果想ThreadLocal线程局部变量有一个除null以外的初始值, * 必须通过子类继承{@code ThreadLocal} 的方式去重写此方法 * 通常, 可以通过匿名内部类的方式实现 * * @return 当前ThreadLocal的初始值 */ protected T initialValue() { return null; }

6. ThreadLocal 注意移除数据

当我们对于 ThreadLocal 中的value值资源对象,使用完毕的时候,一定要执行ThreadLocal.remove()对象方法的移除绑定在ThreadLocal中的资源信息。因为一般ThreadLocal的使用场景都是在 多线程的,而多线程一般都是使用线程池管理线程的。就会存在一个问题?

我们通过上述账户转账案例来讲解这个问题:

我们的运行环境是在Tomcat10,Tomcat10本身就是一个多线程的。

如下当我们对应一个数据库操作完以后,我们需要将对应的资源释放。最后使用的最先关闭,分开 try,防止关闭资源的时候出现异常导致其他资源没有关闭。

如下:我们的ThreadLocal对应的 value 是 Connection 对象

 

java

复制代码

// 创建 ThreadLocal 容器存储绑定线程相关的 信息 private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); /** * 这里没有使用数据库连接池,直接创建连接对象 */ public static Connection getConnection() { Connection connection = threadLocal.get(); // 从ThreadLocal容器中获取 try { // 第一次ThreadLocal 是为空的 if (connection == null) { connection = DriverManager.getConnection(url, user, password); threadLocal.set(connection); } } catch (SQLException e) { throw new RuntimeException(e); } return connection; } if (connection != null) { try { connection.close(); threadLocal.remove(); // 注意关闭资源的时候需要将绑定在threadLocal移除 } catch (SQLException e) { throw new RuntimeException(e); } }

当我们将Connection 资源关闭了,但是:其中该Connection 存储在ThreadLocal当中value值,却是还存储着是为 当前Connection 关闭了的对象的。由于Tomcat是多线程,其中Tomcat服务器是内置了一个线程池的。线程池中的多线程对象是有限的, 这样线程对象 t1,t2,t3 都是提前创建好的,也就是说,t1,t2,t3,是在重复使用的,如果你没有将其 ThreadLocal.remove( 移除掉), 当新的用户,一个新的线程,出现的时候,可能会获取到上一个Connection 已经关闭了的对象,t1线程对象。从而导致的结果就是 这个新用户使用的是 t1 这个对应上的Connection 对象已经关闭了,出现错误。

所以对于: ThreadLocal 中的value值资源对象,使用完毕的时候,一定要执行ThreadLocal.remove()对象方法的移除绑定在ThreadLocal中的资源信息

7. ThreadLocal 内存泄漏

1 . 没有手动删除这个 Entry 2 . CurrentThread 当前线程依然运行

​ 第一点很好理解,只要在使用完下 ThreadLocal ,调用其 remove 方法删除对应的 Entry ,就能避免内存泄漏。 ​ 第二点稍微复杂一点,由于ThreadLocalMap 是 Thread 的一个属性,被当前线程所引用,所以ThreadLocalMap的生命周期跟 Thread 一样长。如果threadlocal变量被回收,那么当前线程的threadlocal 变量副本指向的就是key=null, 也即entry(null,value),那这个entry对应的value永远无法访问到。实际私用ThreadLocal场景都是采用线程池,而线程池中的线程都是复用的,这样就可能导致非常多的entry(null,value)出现,从而导致内存泄露。 综上, ThreadLocal 内存泄漏的根源是: 由于ThreadLocalMap 的生命周期跟 Thread 一样长,对于重复利用的线程来说,如果没有手动删除(remove()方法)对应 key 就会导致entry(null,value)的对象越来越多,从而导致内存泄漏.

8. 正确的使用ThreadLocal

  1. 将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露
  2. 每次使用完ThreadLocal,都调用它的remove()方法,清除数据。

9. ThreadLocal 常见使用场景

如上文所述,ThreadLocal 适用于如下两种场景

  1. 每个线程需要有自己单独的实例
  2. 实例需要在多个方法中共享,但不希望被多线程共享

对于第一点,每个线程拥有自己实例,实现它的方式很多。例如可以在线程内部构建一个单独的实例。ThreadLoca 可以以非常方便的形式满足该需求。

对于第二点,可以在满足第一点(每个线程有自己的实例)的条件下,通过方法间引用传递的形式实现。ThreadLocal 使得代码耦合度更低,且实现更优雅。

10. 案例:MVC三层架构 + 面向接口编程 + ThreadLocal 事务处理实:现用户转账功能的优化

如下是我们对于上篇MVC三层架构🔜🔜🔜  MVC 三层架构案例详细讲解_ChinaRainbowSea的博客-优快云博客 存在事务安全问题的优化:

对应的包架构:

  • resources: 表示一些资源:比如这里是一些连接数据库的一些配置信息:
 

java

复制代码

driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mvc user=root password=MySQL

  • lib : 该项目所需要的依赖:注意:该目录名一定要为:lib,不然Tomcat 无法识别到,该目录一定要在web/WEB-INF/下,不然Tomcat 无法识别到的。

  • M(Model 模型层/业务逻辑处理层)
    • utils : 工具包,这里使用一个数据库连接的工具包。
    • dao : 表示对应XXx数据表的业务逻辑上的处理(增删改查)。面向接口编程:AccountDao,接口定义规范,这里的AccoutDao 定义了对于这张account数据表的业务逻辑上的操作规范。
      • Impl: 表示实现的接口的类:AccountDaoImplImpl命名为后缀,表示接口的实现类。这是大家共识的一种规范。
    • exceptions : 表示对应业务上自定义的异常。
    • javaBean: 表示对应的封装数据的实体类。
    • service: 表示对应的业务逻辑的处理。面向接口编程:AccountService,接口定义规范,这里的AccountService定义了对于这张account数据表的业务逻辑处理上的操作规范,可能需要多个 Dao同时配合。获取多个Service 之间相互配合。
      • Impl: 表示实现的接口的类:AccountServiceImplImpl命名为后缀,表示接口的实现类。这是大家共识的一种规范。
  • C(Controller 控制层):对应 M层,V的之间的桥梁,进行一个调度处理,本身仅仅只做一个调度,不进行业务的处理。比如一个事情:需要调度M层进行处理,同时需要将该M层处理的结果,通过调度V层显示给用户。

10.1 M(Model 模型层/业务逻辑处理层)

 

java

复制代码

package com.RainbowSea.bank.utils; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ResourceBundle; public class DBUtil { // resourceBundle 只能读取到 properties 后缀的文件,注意不要加文件后缀名 private static ResourceBundle resourceBundle = ResourceBundle.getBundle("resources/jdbc"); private static String driver = resourceBundle.getString("driver"); private static String url = resourceBundle.getString("url"); private static String user = resourceBundle.getString("user"); private static String password = resourceBundle.getString("password"); // DBUtil 类加载注册驱动 static { try { Class.forName(driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } // 将构造器私有化,不让创建对象,因为工具类中的方法都是静态的,不需要创建对象 // 为了防止创建对象,故将构造方法私有化 private DBUtil() { } // 创建 ThreadLocal 容器存储绑定线程相关的 信息 private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); /** * 这里没有使用数据库连接池,直接创建连接对象 */ public static Connection getConnection() { Connection connection = threadLocal.get(); // 从ThreadLocal容器中获取 try { // 第一次ThreadLocal 是为空的 if (connection == null) { connection = DriverManager.getConnection(url, user, password); threadLocal.set(connection); } } catch (SQLException e) { throw new RuntimeException(e); } return connection; } /** * 资源的关闭 * 最后使用的最先关闭,逐个关闭,防止存在没有关闭的 */ public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) { if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { throw new RuntimeException(e); } } if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { throw new RuntimeException(e); } } if (connection != null) { try { connection.close(); threadLocal.remove(); // 注意关闭资源的时候需要将绑定在threadLocal移除 } catch (SQLException e) { throw new RuntimeException(e); } } } }

 

java

复制代码

package com.RainbowSea.bank.javabeen; import java.io.Serializable; import java.util.Objects; /** * 账户实体类,封装账户信息的 * 一般是一张表一个。 * pojo 对象 * 有的人也会把这种专门封装数据的对象,称为:"bean对象" (javabean对象,咖啡豆) * 有的人也会把这种专门封装数据的对象,称为领域模型对象,domain对象 * 不同的程序员不同的习惯。 */ public class Account implements Serializable { // 这种普通的简单的对象被成为pojo对象 // 注意我们这里定义的数据类型,使用引用数据类型 // 因为我们数据库中可能存在 null 值,而基本数据类型是不可以存储 null值的 private Long id = null; // id private String actno; // 账号 private Double balance; // 余额 // 反序列化 private static final long serialVersionUID = 1L; public Account() { } public Account(Long id, String actno, Double balance) { this.id = id; this.actno = actno; this.balance = balance; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getActno() { return actno; } public void setActno(String actno) { this.actno = actno; } public Double getBalance() { return balance; } public void setBalance(Double balance) { this.balance = balance; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Account)) return false; Account account = (Account) o; return Objects.equals(getId(), account.getId()) && Objects.equals(getActno(), account.getActno()) && Objects.equals(getBalance(), account.getBalance()); } @Override public int hashCode() { return Objects.hash(getId(), getActno(), getBalance()); } @Override public String toString() { return "Account{" + "id=" + id + ", actno='" + actno + '\'' + ", balance=" + balance + '}'; } }

 

java

复制代码

package com.RainbowSea.bank.dao; import com.RainbowSea.bank.javabeen.Account; import java.util.List; public interface AccountDao { /** * 插入数据 * * @param account * @return */ public int insert(Account account); /** * 通过Id删除数据 * * @param id * @return */ public int deleteById(String id); /** * 更新数据 * * @param account * @return */ public int update(Account account); /** * 通过 actno 查找账户信息 * * @param actno * @return */ public Account selectByActno(String actno); /** * 查询所有的账户信息 * * @return */ public List<Account> selectAll(); }

 

java

复制代码

package com.RainbowSea.bank.dao.impl; import com.RainbowSea.bank.dao.AccountDao; import com.RainbowSea.bank.javabeen.Account; import com.RainbowSea.bank.utils.DBUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; /** * AccountDao 是负责Account 数据的增上改查 * <p> * 1. 什么是DAO ? * Data Access Object (数据访问对象) * 2. DAO实际上是一种设计模式,属于 JavaEE的设计模式之一,不是 23种设计模式 * 3.DAO只负责数据库表的CRUD ,没有任何业务逻辑在里面 * 4.没有任何业务逻辑,只负责表中数据增上改查的对象,有一个特俗的称谓:DAO对象 * 5. 为什么叫做 AccountDao 呢? * 这是因为DAO是专门处理t_act 这张表的 * 如果处理t_act 表的话,可以叫做:UserDao * 如果处理t-student表的话,可以叫做 StudentDao * <p> * int insert() ; * int deleteByActno(); * int update() ; * Account selectByActno(); * List<Account> selectAll(); */ public class AccountDaoImpl implements AccountDao { /** * 插入数据 * * @param account * @return */ public int insert(Account account) { Connection connection = DBUtil.getConnection(); PreparedStatement preparedStatement = null; int count = 0; try { String sql = "insert into t_act(actno,balance) values(?,?)"; preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, account.getActno()); preparedStatement.setDouble(2, account.getBalance()); count = preparedStatement.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(e); } finally { DBUtil.close(connection, preparedStatement, null); } return count; } /** * 通过Id删除数据 * * @param id * @return */ public int deleteById(String id) { Connection connection = DBUtil.getConnection(); int count = 0; PreparedStatement preparedStatement = null; try { String sql = "delete from t_act where id = ?"; preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, id); count = preparedStatement.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(e); } finally { DBUtil.close(connection, preparedStatement, null); } return count; } /** * 更新数据 * * @param account * @return */ public int update(Account account) { PreparedStatement preparedStatement = null; Connection connection = DBUtil.getConnection(); // 从 ThreadLocal中获取到的 int count = 0; System.out.println("update: "+connection); try { String sql = "update t_act set balance = ?, actno = ? where id = ?"; preparedStatement = connection.prepareStatement(sql); //注意设置的 set类型要保持一致。 preparedStatement.setDouble(1, account.getBalance()); preparedStatement.setString(2, account.getActno()); preparedStatement.setLong(3, account.getId()); count = preparedStatement.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(e); } finally { DBUtil.close(null, preparedStatement, null); } return count; } /** * 通过 actno 查找账户信息 * * @param actno * @return */ public Account selectByActno(String actno) { PreparedStatement preparedStatement = null; ResultSet resultSet = null; Account account = new Account(); Connection connection = DBUtil.getConnection(); // 从 ThreadLocal中获取到的 System.out.println("selectByActno :" + connection); try { String sql = "select id,actno,balance from t_act where actno = ?"; preparedStatement = connection.prepareStatement(sql); //注意设置的 set类型要保持一致。 preparedStatement.setString(1, actno); resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { Long id = resultSet.getLong("id"); Double balance = resultSet.getDouble("balance"); // 将结果集封装到java 对象中 account.setActno(actno); account.setId(id); account.setBalance(balance); } } catch (SQLException e) { throw new RuntimeException(e); } finally { DBUtil.close(null, preparedStatement, resultSet); } return account; } /** * 查询所有的账户信息 * * @return */ public List<Account> selectAll() { Connection connection = DBUtil.getConnection(); PreparedStatement preparedStatement = null; ResultSet resultSet = null; List<Account> list = null; try { String sql = "select id,actno,balance from t_act"; preparedStatement = connection.prepareStatement(sql); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { String actno = resultSet.getString("actno"); Long id = resultSet.getLong("id"); Double balance = resultSet.getDouble("balance"); // 将结果集封装到java 对象中 Account account = new Account(id,actno,balance); // 添加到List集合当中 list.add(account); } } catch (SQLException e) { throw new RuntimeException(e); } finally { DBUtil.close(connection, preparedStatement, resultSet); } return list; } }

 

java

复制代码

package com.RainbowSea.bank.exceptions; /** * 余额不足异常 */ public class AppException extends Exception{ public AppException() { } public AppException(String msg) { super(msg); } }

 

java

复制代码

package com.RainbowSea.bank.exceptions; /** * 余额不足异常 */ public class MoneyNotEnoughException extends Exception{ public MoneyNotEnoughException() { } public MoneyNotEnoughException(String msg) { super(msg); } }

 

java

复制代码

package com.RainbowSea.bank.service; import com.RainbowSea.bank.exceptions.AppException; import com.RainbowSea.bank.exceptions.MoneyNotEnoughException; public interface AccountService { public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException; }

 

java

复制代码

package com.RainbowSea.bank.service.impl; import com.RainbowSea.bank.dao.AccountDao; import com.RainbowSea.bank.dao.impl.AccountDaoImpl; import com.RainbowSea.bank.exceptions.AppException; import com.RainbowSea.bank.exceptions.MoneyNotEnoughException; import com.RainbowSea.bank.javabeen.Account; import com.RainbowSea.bank.service.AccountService; import com.RainbowSea.bank.utils.DBUtil; import java.sql.Connection; import java.sql.SQLException; /** * service 翻译为:业务。 * AccountService 专门处理Account业务的一个类 * 在该类中应该编写纯业务代码。(只专注域业务处理,不写别的,不和其他代码混合在一块) * 只希望专注业务,能够将业务完美实现,少量bug. * <p> * 业务类一般起名:XXXService,XXXBiz... */ public class AccountServiceImpl implements AccountService { // 这里的方法起名,一定要体现出,你要处理的是什么业务: // 我们要提供一个能够实现转账的业务的方法(一个业务对应一个方法) // 比如:UserService StudentService OrderService // 处理Account 转账业务的增删改查的Dao private AccountDao accountDao = new AccountDaoImpl(); // 多态:父类的引用指向子类 /** * 完成转账的业务逻辑 * * @param fromActno 转出账号 * @param toActno 转入账号 * @param money 转账金额 */ public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException { Thread thread = Thread.currentThread(); // 获取当前线程 System.out.println("seervice tranfer: " + thread); Connection connection = DBUtil.getConnection(); // 从ThreadLocal获取到的 // service 层控制事务: // 事务的控制需要 Connection 对象 try { // 自动管理,会自动关闭资源 // 开启事务 connection.setAutoCommit(false); System.out.println("service transfer: " + connection); // 查询余额是否充足 Account fromAct = accountDao.selectByActno(fromActno); if (fromAct.getBalance() < money) { throw new MoneyNotEnoughException("对不起,余额不足"); } // 程序到这里说明余额充足 Account toAct = accountDao.selectByActno(toActno); // 修改金额,先从内存上修改,再从硬盘上修改 fromAct.setBalance(fromAct.getBalance() - money); toAct.setBalance(toAct.getBalance() + money); // 从硬盘数据库上修改 int count = accountDao.update(fromAct); // 模拟异常 /* String s = null; s.toString();*/ count += accountDao.update(toAct); if (count != 2) { throw new AppException("账户转账异常,请联系管理员"); } // 提交事务 connection.commit(); } catch (SQLException e) { // 事务的回滚 // 因为我们这里是失败了,是不会提交数据的,数据库也就不会发生改变了。 throw new AppException("账户异常,请联系管理员"); } finally { // 关闭资源,移除ThreadLocal当中绑定的 Connection 对象 DBUtil.close(connection, null, null); } } }

10.2 C(Controller 控制层)

 

java

复制代码

package com.RainbowSea.bank.web; import com.RainbowSea.bank.exceptions.AppException; import com.RainbowSea.bank.exceptions.MoneyNotEnoughException; import com.RainbowSea.bank.service.AccountService; import com.RainbowSea.bank.service.impl.AccountServiceImpl; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; /** * 账户小程序 * AccountServlet 是一个司令官,他负责调度其他组件来完成任务。 * */ @WebServlet("/transfer") public class AccountServlet extends HttpServlet { // AccountServlet 作为一个 Controller 司令官 @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取数据 String fromActno = request.getParameter("fromActno"); String toActno = request.getParameter("toActno"); double money = Double.parseDouble(request.getParameter("money")); // 调用业务方法处理业务(调度Model处理业务,其中是对应数据表的 CRUD操作) AccountService accountService = new AccountServiceImpl(); // 多态 父类的引用指向子类 try { accountService.transfer(fromActno,toActno,money); // 执行到这里说明,成功了, // 展示处理结束(调度 View 做页面展示) response.sendRedirect(request.getContextPath()+"/success.jsp"); } catch (MoneyNotEnoughException e) { // 执行到种类,说明失败了,(余额不足 // 展示处理结束(调度 View 做页面展示) response.sendRedirect(request.getContextPath()+"/error.jsp"); } catch (AppException e) { // 执行到种类,说明失败了,转账异常 // 展示处理结束(调度 View 做页面展示) response.sendRedirect(request.getContextPath()+"/error.jsp"); } // 页面的展示 (调度View做页面展示) } }

10.3 V(View 显示层)

 

java

复制代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>银行账号转账</title> </head> <body> <form action="<%=request.getContextPath()%>/transfer" method="post"> 转出账户: <input type="text" name="fromActno" /> <br> 转入账户: <input type="text" name="toActno" /> <br> 转账金额: <input type="text" name="money" /><br> <input type="submit" value="转账" /> </form> </body> </html>

 

java

复制代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>转账成功</title> </head> <body> <h3>转账成功</h3> </body> </html>

 

java

复制代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>转账失败</title> </head> <body> <h3>转账失败</h3> </body> </html>

10.4 测试

11. ThreadLocal与Synchronized的区别

ThreadLocal其实是与线程绑定的一个变量。 ThreadLocal和Synchonized都用于解决多线程并发访问。

但是ThreadLocal与synchronized有本质的区别:

  1. Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

  2. Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。

  3. 而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

一句话理解ThreadLocal,threadlocl是作为当前线程中属性ThreadLocalMap集合中的某一个Entry的key值Entry(threadlocl,value),虽然不同的线程之间threadlocal这个key值是一样,但是不同的线程所拥有的ThreadLocalMap是独一无二的,也就是不同的线程间同一个ThreadLocal(key)对应存储的值(value)不一样,从而到达了线程间变量隔离的目的,但是在同一个线程中这个value变量地址是一样的。

12. ThreadLocal与Thread,ThreadLocalMap之间的关系

Thread、THreadLocal、ThreadLocalMap之间啊的数据关系图

从这个图中我们可以非常直观的看出,ThreadLocalMap其实是Thread线程的一个属性值,而ThreadLocal是维护ThreadLocalMap这个属性指的一个工具类。Thread线程可以拥有多个ThreadLocal维护的自己线程独享的共享变量(这个共享变量只是针对自己线程里面共享)

13. 总结:

  1. ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程 ,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

  2. ThreadLocal的主要用途是实现线程间变量的隔离,表面上他们使用的是同一个ThreadLocal, 但是实际上使用的值value却是自己独有的一份。 简单的说就是:实现一个线程当中的信息对象共用,共享。代替传引用类型参数的方式。

  3. ThreadLocal 常用的方法。

  4. 当我们对于 ThreadLocal 中的value值资源对象,使用完毕的时候,一定要执行ThreadLocal.remove()对象方法的移除绑定在ThreadLocal中的资源信息

作者:RainbowSea
链接:https://juejin.cn/post/7234061982579802169

根据以下帮我写完整的前后端代码 ,还有步骤内容要具体完整 第一部分 电子商务系统设计概述 应用《高级语言程序设计Ⅱ(Java)》、《电子商务网站设计与开发》、《J2EE电子商务系统开发技术》、《数据处理与数据库A》、《管理信息系统》等课程内容设计与开发电子商务系统。“电子商务系统设计”强调从总体结构、系统分析、系统开发这一角度来研究电子商务系统。任何一个电子商务系统都离不开后台数据库的支持,所以本实践将Java技术、Java EE技术以及数据库技术有效的结合起来,开发一个以数据库技术为支持的WEB应用系统。电子商务系统设计的目的要求如下: 一、课程设计目的意义   1. 加深对讲授内容的理解 《J2EE电子商务系统开发技术》理论课强调实用性,注重学生动手能力的培养。本课程设计将有助于加深对《管理信息系统》、《J2EE电子商务系统开发技术》、《数据处理与数据库A》等课程内容的基本概念、基本原理、设计原则数据库操纵方法的理解。   2. 通过电子商务系统设计与开发,掌握电子商务系统设计与开发的关键技术,培养自主开发能力。 电子商务系统设计侧重于理论应用、系统设计程序开发过程。结合实际需要开发一个特定的“电子商务后台管理系统”,使学生能够运用Java EE技术,结合数据库的基本知识设计数据库,掌握技术方法,掌握面向对象程序设计分析的基本思想基本方法,掌握Java EE开发的三结构、能够自主设计并实现小型的软件,最终具备一定的自主开发能力。使同学能够主动查阅与Java EE应用程序开发的相关资料,具备文献检索能力。进而能够与国内外IT行业对人才的需求接轨,为后面的毕业设计将来能够较好地适应社会需求打下基础。 3. 培养自学以及主动解决问题的能力 通过本次设计,使同学能够主动查阅与应用网站相关资料,掌握一些课堂上老师未曾教授的知识,从而达到培养学生自学以及主动解决问题的能力的目的,为后面的毕业设计打下坚实的基础。 二、课程设计实施步骤 同学们可按下列步骤完成题目的设计并写出设计报告。 第一步:问题分析。 查找网上现有的电子商务网站,对网站进行分析,掌握网站主要功能。在对电子商务网站进行调研的基础上,明确本系统完成内容。依据调查结果,进一步分析表达用户的需求,绘制实体关系图。 第二步:数据库设计与实现。包括: 数据库的概念结构(E-R)图: 逻辑与物理结构设计:将E-R图转换为关系模型,及设计数据库中的表、视图(如果使用)、存储过程(如果使用)的结构定义(可以用SQL脚本提供); 设计系统查询功能及要求,写出主要的查询SQL语句。 第三步:系统功能设计与实现,确定系统开发所使用技术方案,确定系统开发的功能结构,描述每一个所完成功能的类之间交互的时序图。 第四步:完成系统开发。使用Spring MVC框架、JPA技术以及mysql数据库,开发系统功能。使用Java EE三结构进行系统开发,即首先开发DAO,完成对数据库的CRUD,然后进行业务逻辑的开发,最后使用HTML等前端技术完成页面开发,并对其中的关键部分编写测试程序。 第五步:实践报告编写:按照报告书格式要求,编写实践报告。 三、课程设计要求 本次课程设计的目标是运用Java EE技术数据库技术以及管理信息系统知识完成一个电子商务后台管理系统的的设计与开发工作。为了巩固学生掌握的Java EE技术,课程设计采用Spring MVC框架、JPA等技术进行系统开发。数据库不做强制要求,推荐使用mysql。课程设计结束后,学生以文档的形式提交课程设计成果,软件要实际上机检查,要求具有一定的实用性。 设计报告具体要求如下: (1)系统分析:数据库设计(E-R图)、功能模块图、系统软件技术方案。 (2)系统实施计划:列出系统实施的时间进度安排。 (3)登录界面:登录界面截图,界面程序及系统登录业务逻辑的核心程序。 (4)系统首页:系统首页截图,系统首页核心程序。 (5)用户管理功能:实现用户列表的展示、用户信息添加、用户修改、删除及查询、展示系统截图、绘制类之间相互调用的时序图及核心程序。 (6)商品管理功能:实现商品列表的展示、商品添加、商品修改以及删除查询,展示系统截图、绘制类之间相互调用的时序图及核心程序。 第二部分 课程设计指导 一、项目开发的一般流程 1.需求确定 通过各种手段(头脑风暴、会议、询问、原型——界面原型业务原型)确定系统的功能性能。 2.分析与设计 1)架构分析与设计 2)业务逻辑分析 3)业务逻辑设计 4)界面设计 3.开发环境搭建 4.开发-测试-开发-测试(螺旋递增式开发) 5.文档编写 二、需求分析 1.问题的提出: 传统的网站管理方式有两种:一是静态HTML页面,更新信息时需要重新制作页面,然后上传页面并修改相应的链接,这种方式因为效率太低已不多用;二是基于J2EE脚本语言,将动态网页数据库结合,通过应用程序来处理程序,这是目前较为流行的做法。网络广告系统充分发挥网络的优势,实现了网络广告的动态管理,使得对网络广告的管理更加方便、及时、安全,降低了升级维护的难度,提高了工作效率。 2.系统需要解决的问题及功能: 根据实现业务的不同进行功能分析。 三、系统分析与设计 1.架构分析与设计 逻辑架构:3架构、n架构;MVC等; 物理架构:Web服务器的分布;数据库服务器的分布; 技术解决方案的确定: PHP;Open Source; 2.业务逻辑分析 (Unified Process,Use Case) -根据需求分析业务逻辑 -他们会使用本系统做什么 -通常他们使用本系统的步骤是什么样的 -会有哪些明显的类来支撑本系统的运行 -会有哪些不同的提示返回给用户 本阶段与需求的确定密切相关,通常在确定需求的时候会进行相关的分析 3.业务逻辑设计 -根据需求的分析来确定具体的类 -确定类的属性 -确定类的接口(方法) -确定类之间的关系 -确定用户操作流程在设计上的反映 -进行数据库的设计(不同的项目步骤可能不尽相同) 4.界面设计 -设计系统的界面风格: 颜色,style -设计系统的具体“模拟”界面 能够从头走到尾:方便进行需求的确定;方便程序员的开发 (界面设计:注意命名机制,包括文件名、字段名等;风格要统一,最好画图表示出来,不用文字) 四、开发环境搭建 -开发工具的确定 -配置管理工具的确定 -测试工具的确定 -文件服务器/配置服务器的确定 五、开发-测试-开发-测试 -按照设计进行开发 -迅速开发原型 -进行迭代开发 -提早进行测试 -单元测试 -黑盒测试 -性能测试 -应用性测试 第三部分 设计描述 本部分讲述如何用Java EE技术开发一个简易的电子商务后台系统,其目的是掌握一般的信息系统开发的常用方法,以及使学生熟练掌握基于Java EE开发系统的过程及技术的使用。系统采用Spring MVC框架、JPA技术等;本系统采用的是MySql数据库。 一、系统模块构成 本次实践所完成的主要功能模块主要包括一下部分: (1)用户管理模块 包括用户会员的添加、删除以及修改查询。 (2)商品管理模块 包括商品的添加、删除以及修改查询。 (3)库存查询 主要查询每一件商品的当前库存数量。 (4)权限管理 实现用户的功能权限的过滤管理。 系统模块如下图所示: 二、数据库设计 在这个阶段,根据所了解掌握的用户需求,进行了数据的采集对数据的处理操作,以确保数据采集的详细准确,理清数据库中各个数据项的关系,这将为系统的设计打基础。在数据分析阶段要做到两点: 调查清楚应用系统用户所需要操作的数据,决定存储什么数据。 调查清楚应用系统用户要求对数据进行什么样的处理,理清各个数据项之间的关系。 注意做到这两点是十分重要的要向系统用户详细调查保证信息的采集的完整性、一致性准确性。在数据分析后要做到设计出一个数据字典文档包括三方面: (1) 数据项:包括字段名、字段的含义、类型定义以及其他数据项的逻辑关系。 (2) 数据结构:若干个数据项的有意义的集合,包括字段名称、含义以及组成的数据结构的数据项。 (3) 数据流:指数据库中数据的处理过程,包括数据信息的输入、处理输出。 据此,可归结出内容管理系统所需完成的主体任务: (1) 基本信息的添加、修改删除:包括用户商品等信息。 (2) 基本信息的查询:商品信息的查询。 针对内容管理系统的总体需求,通过对系统的内容数据流程分析与系统总体功能模块梳理,主要的实体关系图如下图所示: 三、技术方案 本系统是基于Java EE技术,采用B/S结构,B/S体系结构的客户端是Web浏览器,它负责实现显示交互。应用服务器是位于Web服务器端,它的任务是接受用户的请求,执行相应的应用程序并与数据库进行连接,通过SQL等方式向数据库服务器提出数据处理申请,并将数据处理的结果提交给Web服务器,再由Web服务器传送回客户端。数据库服务器负责接受Web服务器对数据操作的请求,实现对数据库查询、修改、更新等功能,把运行结果提交给Web服务器。如下图所示,是基于B/S的三体系结构。 第四部分 实验内容 一、用户管理(核心实现部分) 1.1 实体类: package cn.edu.xiyou; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import static javax.persistence.GenerationType.IDENTITY; import javax.persistence.Id; import javax.persistence.Table; /** * Teacheruser entity. @author MyEclipse Persistence Tools */ @Entity @Table(name = "teacheruser", catalog = "teacher") public class Teacheruser implements java.io.Serializable { // Fields private Integer id; private String name; private Integer age; private String password; // Constructors /** default constructor */ public Teacheruser() { } /** full constructor */ public Teacheruser(String name, Integer age) { this.name = name; this.age = age; } // Property accessors @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "id", unique = true, nullable = false) public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } @Column(name = "name", length = 32) public String getName() { return this.name; } public void setName(String name) { this.name = name; } @Column(name = "age") public Integer getAge() { return this.age; } public void setAge(Integer age) { this.age = age; } @Column(name = "password", length = 32) public String getPassWord() { return this.password; } public void setPassWord(String name) { this.password = name; } } 1.2 DAO接口(CRUD) package cn.edu.xiyou; import java.util.List; /** * Interface for TeacheruserDAO. * * @author MyEclipse Persistence Tools */ public interface ITeacheruserDAO { /** * Perform an initial save of a previously unsaved Teacheruser entity. All * subsequent persist actions of this entity should use the #update() * method. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#persist(Object) * EntityManager#persist} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * ITeacheruserDAO.save(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Teacheruser entity to persist * @throws RuntimeException * when the operation fails */ public void save(Teacheruser entity); /** * Delete a persistent Teacheruser entity. This operation must be performed * within the a database transaction context for the entity's data to be * permanently deleted from the persistence store, i.e., database. This * method uses the {@link javax.persistence.EntityManager#remove(Object) * EntityManager#delete} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * ITeacheruserDAO.delete(entity); * EntityManagerHelper.commit(); * entity = null; * </pre> * * @param entity * Teacheruser entity to delete * @throws RuntimeException * when the operation fails */ public void delete(Teacheruser entity); /** * Persist a previously saved Teacheruser entity and return it or a copy of * it to the sender. A copy of the Teacheruser entity parameter is returned * when the JPA persistence mechanism has not previously been tracking the * updated entity. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * entity = ITeacheruserDAO.update(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Teacheruser entity to update * @return Teacheruser the persisted Teacheruser entity instance, may not be * the same * @throws RuntimeException * if the operation fails */ public Teacheruser update(Teacheruser entity); public Teacheruser findById(Integer id); /** * Find all Teacheruser entities with a specific property value. * * @param propertyName * the name of the Teacheruser property to query * @param value * the property value to match * @return List<Teacheruser> found by query */ public List<Teacheruser> findByProperty(String propertyName, Object value); public List<Teacheruser> findByName(Object name); public List<Teacheruser> findByAge(Object age); /** * Find all Teacheruser entities. * * @return List<Teacheruser> all Teacheruser entities */ public List<Teacheruser> findAll(); } 1.3 DAO实现Dao接口中的方法) package cn.edu.xiyou; import java.util.List; import java.util.logging.Level; import javax.persistence.EntityManager; import javax.persistence.Query; /** * A data access object (DAO) providing persistence and search support for * Teacheruser entities. Transaction control of the save(), update() and * delete() operations must be handled externally by senders of these methods or * must be manually added to each of these methods for data to be persisted to * the JPA datastore. * * @see cn.edu.xiyou.Teacheruser * @author MyEclipse Persistence Tools */ public class TeacheruserDAO implements ITeacheruserDAO { // property constants public static final String NAME = "name"; public static final String AGE = "age"; private EntityManager getEntityManager() { return EntityManagerHelper.getEntityManager(); } /** * Perform an initial save of a previously unsaved Teacheruser entity. All * subsequent persist actions of this entity should use the #update() * method. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#persist(Object) * EntityManager#persist} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * TeacheruserDAO.save(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Teacheruser entity to persist * @throws RuntimeException * when the operation fails */ public void save(Teacheruser entity) { EntityManagerHelper .log("saving Teacheruser instance", Level.INFO, null); try { EntityManager em = getEntityManager(); em.getTransaction().begin(); em.persist(entity); em.flush(); em.getTransaction().commit(); EntityManagerHelper.log("save successful", Level.INFO, null); } catch (RuntimeException re) { EntityManagerHelper.log("save failed", Level.SEVERE, re); throw re; } } /** * Delete a persistent Teacheruser entity. This operation must be performed * within the a database transaction context for the entity's data to be * permanently deleted from the persistence store, i.e., database. This * method uses the {@link javax.persistence.EntityManager#remove(Object) * EntityManager#delete} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * TeacheruserDAO.delete(entity); * EntityManagerHelper.commit(); * entity = null; * </pre> * * @param entity * Teacheruser entity to delete * @throws RuntimeException * when the operation fails */ public void delete(Teacheruser entity) { EntityManagerHelper.log("deleting Teacheruser instance", Level.INFO, null); try { entity = getEntityManager().getReference(Teacheruser.class, entity.getId()); getEntityManager().remove(entity); EntityManagerHelper.log("delete successful", Level.INFO, null); } catch (RuntimeException re) { EntityManagerHelper.log("delete failed", Level.SEVERE, re); throw re; } } /** * Persist a previously saved Teacheruser entity and return it or a copy of * it to the sender. A copy of the Teacheruser entity parameter is returned * when the JPA persistence mechanism has not previously been tracking the * updated entity. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * entity = TeacheruserDAO.update(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Teacheruser entity to update * @return Teacheruser the persisted Teacheruser entity instance, may not be * the same * @throws RuntimeException * if the operation fails */ public Teacheruser update(Teacheruser entity) { EntityManagerHelper.log("updating Teacheruser instance", Level.INFO, null); try { EntityManager em = getEntityManager(); em.getTransaction().begin(); Teacheruser result = em.merge(entity); em.getTransaction().commit(); EntityManagerHelper.log("update successful", Level.INFO, null); return result; } catch (RuntimeException re) { EntityManagerHelper.log("update failed", Level.SEVERE, re); throw re; } } public Teacheruser findById(Integer id) { EntityManagerHelper.log("finding Teacheruser instance with id: " + id, Level.INFO, null); try { Teacheruser instance = getEntityManager().find(Teacheruser.class, id); return instance; } catch (RuntimeException re) { EntityManagerHelper.log("find failed", Level.SEVERE, re); throw re; } } /** * Find all Teacheruser entities with a specific property value. * * @param propertyName * the name of the Teacheruser property to query * @param value * the property value to match * @return List<Teacheruser> found by query */ @SuppressWarnings("unchecked") public List<Teacheruser> findByProperty(String propertyName, final Object value) { EntityManagerHelper.log("finding Teacheruser instance with property: " + propertyName + ", value: " + value, Level.INFO, null); try { final String queryString = "select model from Teacheruser model where model." + propertyName + "= :propertyValue"; Query query = getEntityManager().createQuery(queryString); query.setParameter("propertyValue", value); return query.getResultList(); } catch (RuntimeException re) { EntityManagerHelper.log("find by property name failed", Level.SEVERE, re); throw re; } } public List<Teacheruser> findByName(Object name) { return findByProperty(NAME, name); } public List<Teacheruser> findByAge(Object age) { return findByProperty(AGE, age); } /** * Find all Teacheruser entities. * * @return List<Teacheruser> all Teacheruser entities */ @SuppressWarnings("unchecked") public List<Teacheruser> findAll() { EntityManagerHelper.log("finding all Teacheruser instances", Level.INFO, null); try { final String queryString = "select model from Teacheruser model"; Query query = getEntityManager().createQuery(queryString); return query.getResultList(); } catch (RuntimeException re) { EntityManagerHelper.log("find all failed", Level.SEVERE, re); throw re; } } } 1.4 Controller package cn.edu.xiyou.controller; import java.util.List; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import cn.edu.xiyou.Teacheruser; import cn.edu.xiyou.TeacheruserDAO; @Controller public class TestController { TeacheruserDAO td = new TeacheruserDAO(); @RequestMapping("/test.do") public String test(String username,String password,Model model){ System.out.println("the username is ==="+username); System.out.println("the password is ==="+password); List<Teacheruser> tus = td.findByName(username); Teacheruser tu = tus.get(0); if(tu.getPassWord().equals(password)){ System.out.println("login success"); model.addAttribute("result","success"); }else{ System.out.println("login filure"); model.addAttribute("result","failure"); } return "hello"; } @RequestMapping("/test2.do") public String test2(String username2,String password2,String age,Model model){ System.out.println("the username is ==="+username2); System.out.println("the password is ==="+password2); Teacheruser tu = new Teacheruser(); tu.setAge(Integer.parseInt(age)); tu.setName(username2); tu.setPassWord(password2); td.save(tu); model.addAttribute("result",“成功”); return "hello2"; } @RequestMapping("/login.do") public String login(){ return "userlogin"; } @RequestMapping("/userverify.do") public String userLogin(String username,String password){ System.out.println("the username is ==="+username); System.out.println("the password is ==="+password); return ""; } } 二、Spring MVC配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <context:component-scan base-package="cn.edu.xiyou" /> <mvc:annotation-driven/> <!-- View Resolver --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/WEB-INF/view/" /> <property name="suffix" value=".jsp" /> </bean> <bean id="viewNameTranslator" class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator"/> <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/> </beans> 三、JPA配置文件 <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="testJPA"> <class>cn.edu.xiyou.Teacheruser</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.cache.use_second_level_cache" value="false" /> <property name="hibernate.cache.use_query_cache" value="false" /> <property name="hibernate.connection.url" value="jdbc:mysql://127.0.0.1:3306/teacher"/> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> <property name="hibernate.connection.username" value="root"/> <property name="hibernate.connection.password" value="123321"/> </properties> </persistence-unit> </persistence> 四、JPA基础类 package cn.edu.xiyou; import java.util.logging.Level; import java.util.logging.Logger; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.Query; /** * @author MyEclipse Persistence Tools */ public class EntityManagerHelper { private static final EntityManagerFactory emf; private static final ThreadLocal<EntityManager> threadLocal; private static final Logger logger; static { emf = Persistence.createEntityManagerFactory("testJPA"); threadLocal = new ThreadLocal<EntityManager>(); logger = Logger.getLogger("testJPA"); logger.setLevel(Level.ALL); } public static EntityManager getEntityManager() { EntityManager manager = threadLocal.get(); if (manager == null || !manager.isOpen()) { manager = emf.createEntityManager(); threadLocal.set(manager); } return manager; } public static void closeEntityManager() { EntityManager em = threadLocal.get(); threadLocal.set(null); if (em != null) em.close(); } public static void beginTransaction() { getEntityManager().getTransaction().begin(); } public static void commit() { getEntityManager().getTransaction().commit(); } public static void rollback() { getEntityManager().getTransaction().rollback(); } public static Query createQuery(String query) { return getEntityManager().createQuery(query); } public static void log(String info, Level level, Throwable ex) { logger.log(level, info, ex); } } 五、用户管理 5.1 实体类: package cn.edu.xupt.accounts; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import cn.edu.xupt.users.Users; /** * Accounts entity. @author MyEclipse Persistence Tools */ @Entity @Table(name = "accounts", catalog = "wsy") public class Accounts implements java.io.Serializable { // Fields private Integer account; private Users users; private Double balance; // Constructors /** default constructor */ public Accounts() { } /** minimal constructor */ public Accounts(Integer account) { this.account = account; } /** full constructor */ public Accounts(Integer account, Users users, Double balance) { this.account = account; this.users = users; this.balance = balance; } // Property accessors @Id @Column(name = "account", unique = true, nullable = false) public Integer getAccount() { return this.account; } public void setAccount(Integer account) { this.account = account; } @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "usersId") public Users getUsers() { return this.users; } public void setUsers(Users users) { this.users = users; } @Column(name = "balance", precision = 22, scale = 0) public Double getBalance() { return this.balance; } public void setBalance(Double balance) { this.balance = balance; } } 5.2 DAO接口 package cn.edu.xupt.accounts; import java.util.List; /** * Interface for AccountsDAO. * * @author MyEclipse Persistence Tools */ public interface IAccountsDAO { /** * Perform an initial save of a previously unsaved Accounts entity. All * subsequent persist actions of this entity should use the #update() * method. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#persist(Object) * EntityManager#persist} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * IAccountsDAO.save(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Accounts entity to persist * @throws RuntimeException * when the operation fails */ public void save(Accounts entity); /** * Delete a persistent Accounts entity. This operation must be performed * within the a database transaction context for the entity's data to be * permanently deleted from the persistence store, i.e., database. This * method uses the {@link javax.persistence.EntityManager#remove(Object) * EntityManager#delete} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * IAccountsDAO.delete(entity); * EntityManagerHelper.commit(); * entity = null; * </pre> * * @param entity * Accounts entity to delete * @throws RuntimeException * when the operation fails */ public void delete(Accounts entity); /** * Persist a previously saved Accounts entity and return it or a copy of it * to the sender. A copy of the Accounts entity parameter is returned when * the JPA persistence mechanism has not previously been tracking the * updated entity. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * entity = IAccountsDAO.update(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Accounts entity to update * @return Accounts the persisted Accounts entity instance, may not be the * same * @throws RuntimeException * if the operation fails */ public Accounts update(Accounts entity); public Accounts findById(Integer id); /** * Find all Accounts entities with a specific property value. * * @param propertyName * the name of the Accounts property to query * @param value * the property value to match * @return List<Accounts> found by query */ public List<Accounts> findByProperty(String propertyName, Object value); public List<Accounts> findByBalance(Object balance); /** * Find all Accounts entities. * * @return List<Accounts> all Accounts entities */ public List<Accounts> findAll(); } 5.3 DAO类 package cn.edu.xupt.accounts; import java.util.List; import java.util.logging.Level; import javax.persistence.EntityManager; import javax.persistence.Query; import cn.edu.xupt.ordersDetails.EntityManagerHelper; /** * A data access object (DAO) providing persistence and search support for * Accounts entities. Transaction control of the save(), update() and delete() * operations must be handled externally by senders of these methods or must be * manually added to each of these methods for data to be persisted to the JPA * datastore. * * @see cn.edu.xupt.accounts.Accounts * @author MyEclipse Persistence Tools */ public class AccountsDAO implements IAccountsDAO { // property constants public static final String BALANCE = "balance"; private EntityManager getEntityManager() { return EntityManagerHelper.getEntityManager(); } /** * Perform an initial save of a previously unsaved Accounts entity. All * subsequent persist actions of this entity should use the #update() * method. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#persist(Object) * EntityManager#persist} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * AccountsDAO.save(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Accounts entity to persist * @throws RuntimeException * when the operation fails */ public void save(Accounts entity) { EntityManagerHelper.log("saving Accounts instance", Level.INFO, null); try { EntityManager em=getEntityManager(); em.getTransaction().begin(); em.persist(entity); em.flush(); em.getTransaction().commit(); } catch (RuntimeException re) { EntityManagerHelper.log("save failed", Level.SEVERE, re); throw re; } } /** * Delete a persistent Accounts entity. This operation must be performed * within the a database transaction context for the entity's data to be * permanently deleted from the persistence store, i.e., database. This * method uses the {@link javax.persistence.EntityManager#remove(Object) * EntityManager#delete} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * AccountsDAO.delete(entity); * EntityManagerHelper.commit(); * entity = null; * </pre> * * @param entity * Accounts entity to delete * @throws RuntimeException * when the operation fails */ public void delete(Accounts entity) { EntityManagerHelper.log("deleting Accounts instance", Level.INFO, null); try { EntityManager em=getEntityManager(); em.getTransaction().begin(); entity = em.getReference(Accounts.class,entity.getAccount()); em.remove(entity); em.getTransaction().commit(); EntityManagerHelper.log("delete successful", Level.INFO, null); } catch (RuntimeException re) { EntityManagerHelper.log("delete failed", Level.SEVERE, re); throw re; } } /** * Persist a previously saved Accounts entity and return it or a copy of it * to the sender. A copy of the Accounts entity parameter is returned when * the JPA persistence mechanism has not previously been tracking the * updated entity. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * entity = AccountsDAO.update(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Accounts entity to update * @return Accounts the persisted Accounts entity instance, may not be the * same * @throws RuntimeException * if the operation fails */ public Accounts update(Accounts entity) { EntityManagerHelper.log("updating Accounts instance", Level.INFO, null); try { EntityManager em=getEntityManager(); em.getTransaction().begin(); Accounts result = getEntityManager().merge(entity); em.getTransaction().commit(); EntityManagerHelper.log("update successful", Level.INFO, null); return result; } catch (RuntimeException re) { EntityManagerHelper.log("update failed", Level.SEVERE, re); throw re; } } public Accounts findById(Integer id) { EntityManagerHelper.log("finding Accounts instance with id: " + id, Level.INFO, null); try { EntityManager em=getEntityManager(); em.getTransaction().begin(); Accounts instance =em.find(Accounts.class, id); em.getTransaction().commit(); return instance; } catch (RuntimeException re) { EntityManagerHelper.log("find failed", Level.SEVERE, re); throw re; } } /** * Find all Accounts entities with a specific property value. * * @param propertyName * the name of the Accounts property to query * @param value * the property value to match * @return List<Accounts> found by query */ @SuppressWarnings("unchecked") public List<Accounts> findByProperty(String propertyName, final Object value) { EntityManagerHelper.log("finding Accounts instance with property: " + propertyName + ", value: " + value, Level.INFO, null); try { final String queryString = "select model from Accounts model where model." + propertyName + "= :propertyValue"; Query query = getEntityManager().createQuery(queryString); query.setParameter("propertyValue", value); return query.getResultList(); } catch (RuntimeException re) { EntityManagerHelper.log("find by property name failed", Level.SEVERE, re); throw re; } } public List<Accounts> findByBalance(Object balance) { return findByProperty(BALANCE, balance); } /** * Find all Accounts entities. * * @return List<Accounts> all Accounts entities */ @SuppressWarnings("unchecked") public List<Accounts> findAll() { EntityManagerHelper.log("finding all Accounts instances", Level.INFO, null); try { final String queryString = "select model from Accounts model"; Query query = getEntityManager().createQuery(queryString); return query.getResultList(); } catch (RuntimeException re) { EntityManagerHelper.log("find all failed", Level.SEVERE, re); throw re; } } } 六、库存管理 6.1 实体类 package cn.edu.xupt.orders; import java.sql.Timestamp; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import cn.edu.xupt.users.Users; /** * Orders entity. @author MyEclipse Persistence Tools */ @Entity @Table(name = "orders", catalog = "wsy") public class Orders implements java.io.Serializable { // Fields private Integer ordersId; private Users users; private Timestamp time; // Constructors /** default constructor */ public Orders() { } /** minimal constructor */ public Orders(Integer ordersId) { this.ordersId = ordersId; } /** full constructor */ public Orders(Integer ordersId, Users users, Timestamp time) { this.ordersId = ordersId; this.users = users; this.time = time; } // Property accessors @Id @Column(name = "ordersId", unique = true, nullable = false) public Integer getOrdersId() { return this.ordersId; } public void setOrdersId(Integer ordersId) { this.ordersId = ordersId; } @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "usersId") public Users getUsers() { return this.users; } public void setUsers(Users users) { this.users = users; } @Column(name = "time", length = 19) public Timestamp getTime() { return this.time; } public void setTime(Timestamp time) { this.time = time; } } 6.2 DAO接口 package cn.edu.xupt.orders; import java.sql.Timestamp; import java.util.List; /** * Interface for OrdersDAO. * * @author MyEclipse Persistence Tools */ public interface IOrdersDAO { /** * Perform an initial save of a previously unsaved Orders entity. All * subsequent persist actions of this entity should use the #update() * method. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#persist(Object) * EntityManager#persist} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * IOrdersDAO.save(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Orders entity to persist * @throws RuntimeException * when the operation fails */ public void save(Orders entity); /** * Delete a persistent Orders entity. This operation must be performed * within the a database transaction context for the entity's data to be * permanently deleted from the persistence store, i.e., database. This * method uses the {@link javax.persistence.EntityManager#remove(Object) * EntityManager#delete} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * IOrdersDAO.delete(entity); * EntityManagerHelper.commit(); * entity = null; * </pre> * * @param entity * Orders entity to delete * @throws RuntimeException * when the operation fails */ public void delete(Orders entity); /** * Persist a previously saved Orders entity and return it or a copy of it to * the sender. A copy of the Orders entity parameter is returned when the * JPA persistence mechanism has not previously been tracking the updated * entity. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * entity = IOrdersDAO.update(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Orders entity to update * @return Orders the persisted Orders entity instance, may not be the same * @throws RuntimeException * if the operation fails */ public Orders update(Orders entity); public Orders findById(Integer id); /** * Find all Orders entities with a specific property value. * * @param propertyName * the name of the Orders property to query * @param value * the property value to match * @return List<Orders> found by query */ public List<Orders> findByProperty(String propertyName, Object value); /** * Find all Orders entities. * * @return List<Orders> all Orders entities */ public List<Orders> findAll(); } 6.3 DAO实现 package cn.edu.xupt.orders; import java.sql.Timestamp; import java.util.List; import java.util.logging.Level; import javax.persistence.EntityManager; import javax.persistence.Query; import cn.edu.xupt.ordersDetails.EntityManagerHelper; /** * A data access object (DAO) providing persistence and search support for * Orders entities. Transaction control of the save(), update() and delete() * operations must be handled externally by senders of these methods or must be * manually added to each of these methods for data to be persisted to the JPA * datastore. * * @see cn.edu.xupt.orders.Orders * @author MyEclipse Persistence Tools */ public class OrdersDAO implements IOrdersDAO { // property constants private EntityManager getEntityManager() { return EntityManagerHelper.getEntityManager(); } /** * Perform an initial save of a previously unsaved Orders entity. All * subsequent persist actions of this entity should use the #update() * method. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#persist(Object) * EntityManager#persist} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * OrdersDAO.save(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Orders entity to persist * @throws RuntimeException * when the operation fails */ public void save(Orders entity) { EntityManagerHelper.log("saving Orders instance", Level.INFO, null); try { getEntityManager().persist(entity); EntityManager em=getEntityManager(); em.getTransaction().begin(); em.persist(entity); em.flush(); em.getTransaction().commit(); } catch (RuntimeException re) { EntityManagerHelper.log("save failed", Level.SEVERE, re); throw re; } } /** * Delete a persistent Orders entity. This operation must be performed * within the a database transaction context for the entity's data to be * permanently deleted from the persistence store, i.e., database. This * method uses the {@link javax.persistence.EntityManager#remove(Object) * EntityManager#delete} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * OrdersDAO.delete(entity); * EntityManagerHelper.commit(); * entity = null; * </pre> * * @param entity * Orders entity to delete * @throws RuntimeException * when the operation fails */ public void delete(Orders entity) { EntityManagerHelper.log("deleting Orders instance", Level.INFO, null); try { EntityManager em=getEntityManager(); em.getTransaction().begin(); entity = em.getReference(Orders.class, entity.getOrdersId()); em.remove(entity); em.getTransaction().commit(); EntityManagerHelper.log("delete successful", Level.INFO, null); } catch (RuntimeException re) { EntityManagerHelper.log("delete failed", Level.SEVERE, re); throw re; } } /** * Persist a previously saved Orders entity and return it or a copy of it to * the sender. A copy of the Orders entity parameter is returned when the * JPA persistence mechanism has not previously been tracking the updated * entity. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * entity = OrdersDAO.update(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Orders entity to update * @return Orders the persisted Orders entity instance, may not be the same * @throws RuntimeException * if the operation fails */ public Orders update(Orders entity) { EntityManagerHelper.log("updating Orders instance", Level.INFO, null); try { EntityManager em=getEntityManager(); em.getTransaction().begin(); Orders result = getEntityManager().merge(entity); em.getTransaction().commit(); EntityManagerHelper.log("update successful", Level.INFO, null); return result; } catch (RuntimeException re) { EntityManagerHelper.log("update failed", Level.SEVERE, re); throw re; } } public Orders findById(Integer id) { EntityManagerHelper.log("finding Orders instance with id: " + id, Level.INFO, null); try { EntityManager em=getEntityManager(); em.getTransaction().begin(); Orders instance =em.find(Orders.class, id); em.getTransaction().commit(); return instance; } catch (RuntimeException re) { EntityManagerHelper.log("find failed", Level.SEVERE, re); throw re; } } /** * Find all Orders entities with a specific property value. * * @param propertyName * the name of the Orders property to query * @param value * the property value to match * @return List<Orders> found by query */ @SuppressWarnings("unchecked") public List<Orders> findByProperty(String propertyName, final Object value) { EntityManagerHelper.log("finding Orders instance with property: " + propertyName + ", value: " + value, Level.INFO, null); try { final String queryString = "select model from Orders model where model." + propertyName + "= :propertyValue"; Query query = getEntityManager().createQuery(queryString); query.setParameter("propertyValue", value); return query.getResultList(); } catch (RuntimeException re) { EntityManagerHelper.log("find by property name failed", Level.SEVERE, re); throw re; } } /** * Find all Orders entities. * * @return List<Orders> all Orders entities */ @SuppressWarnings("unchecked") public List<Orders> findAll() { EntityManagerHelper.log("finding all Orders instances", Level.INFO, null); try { final String queryString = "select model from Orders model"; Query query = getEntityManager().createQuery(queryString); return query.getResultList(); } catch (RuntimeException re) { EntityManagerHelper.log("find all failed", Level.SEVERE, re); throw re; } } } 七、商品管理 7.1 实体类 package cn.edu.xupt.goodsInformation; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; /** * Goodsinformation entity. @author MyEclipse Persistence Tools */ @Entity @Table(name = "goodsinformation", catalog = "wsy") public class Goodsinformation implements java.io.Serializable { // Fields private Integer goodsId; private String goodsName; private Integer goodsNum; private Double goodsPrice; // Constructors /** default constructor */ public Goodsinformation() { } /** minimal constructor */ public Goodsinformation(Integer goodsId) { this.goodsId = goodsId; } /** full constructor */ public Goodsinformation(Integer goodsId, String goodsName, Integer goodsNum, Double goodsPrice) { this.goodsId = goodsId; this.goodsName = goodsName; this.goodsNum = goodsNum; this.goodsPrice = goodsPrice; } // Property accessors @Id @Column(name = "goodsId", unique = true, nullable = false) public Integer getGoodsId() { return this.goodsId; } public void setGoodsId(Integer goodsId) { this.goodsId = goodsId; } @Column(name = "goodsName", length = 16) public String getGoodsName() { return this.goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } @Column(name = "goodsNum") public Integer getGoodsNum() { return this.goodsNum; } public void setGoodsNum(Integer goodsNum) { this.goodsNum = goodsNum; } @Column(name = "goodsPrice", precision = 22, scale = 0) public Double getGoodsPrice() { return this.goodsPrice; } public void setGoodsPrice(Double goodsPrice) { this.goodsPrice = goodsPrice; } } 7.2 DAO接口 package cn.edu.xupt.goodsInformation; import java.util.List; /** * Interface for GoodsinformationDAO. * * @author MyEclipse Persistence Tools */ public interface IGoodsinformationDAO { /** * Perform an initial save of a previously unsaved Goodsinformation entity. * All subsequent persist actions of this entity should use the #update() * method. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#persist(Object) * EntityManager#persist} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * IGoodsinformationDAO.save(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Goodsinformation entity to persist * @throws RuntimeException * when the operation fails */ public void save(Goodsinformation entity); /** * Delete a persistent Goodsinformation entity. This operation must be * performed within the a database transaction context for the entity's data * to be permanently deleted from the persistence store, i.e., database. * This method uses the * {@link javax.persistence.EntityManager#remove(Object) * EntityManager#delete} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * IGoodsinformationDAO.delete(entity); * EntityManagerHelper.commit(); * entity = null; * </pre> * * @param entity * Goodsinformation entity to delete * @throws RuntimeException * when the operation fails */ public void delete(Goodsinformation entity); /** * Persist a previously saved Goodsinformation entity and return it or a * copy of it to the sender. A copy of the Goodsinformation entity parameter * is returned when the JPA persistence mechanism has not previously been * tracking the updated entity. This operation must be performed within the * a database transaction context for the entity's data to be permanently * saved to the persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * entity = IGoodsinformationDAO.update(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Goodsinformation entity to update * @return Goodsinformation the persisted Goodsinformation entity instance, * may not be the same * @throws RuntimeException * if the operation fails */ public Goodsinformation update(Goodsinformation entity); public Goodsinformation findById(Integer id); /** * Find all Goodsinformation entities with a specific property value. * * @param propertyName * the name of the Goodsinformation property to query * @param value * the property value to match * @return List<Goodsinformation> found by query */ public List<Goodsinformation> findByProperty(String propertyName, Object value); public List<Goodsinformation> findByGoodsName(Object goodsName); public List<Goodsinformation> findByGoodsNum(Object goodsNum); public List<Goodsinformation> findByGoodsPrice(Object goodsPrice); /** * Find all Goodsinformation entities. * * @return List<Goodsinformation> all Goodsinformation entities */ public List<Goodsinformation> findAll(); } 7.3 DAO实现 package cn.edu.xupt.goodsInformation; import java.util.List; import java.util.logging.Level; import javax.persistence.EntityManager; import javax.persistence.Query; import cn.edu.xupt.ordersDetails.EntityManagerHelper; /** * A data access object (DAO) providing persistence and search support for * Goodsinformation entities. Transaction control of the save(), update() and * delete() operations must be handled externally by senders of these methods or * must be manually added to each of these methods for data to be persisted to * the JPA datastore. * * @see cn.edu.xupt.goodsInformation.Goodsinformation * @author MyEclipse Persistence Tools */ public class GoodsinformationDAO implements IGoodsinformationDAO { // property constants public static final String GOODS_NAME = "goodsName"; public static final String GOODS_NUM = "goodsNum"; public static final String GOODS_PRICE = "goodsPrice"; public static final String GOODS_ID = "goodsId"; private EntityManager getEntityManager() { return EntityManagerHelper.getEntityManager(); } /** * Perform an initial save of a previously unsaved Goodsinformation entity. * All subsequent persist actions of this entity should use the #update() * method. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#persist(Object) * EntityManager#persist} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * GoodsinformationDAO.save(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Goodsinformation entity to persist * @throws RuntimeException * when the operation fails */ public void save(Goodsinformation entity) { EntityManagerHelper.log("saving Goodsinformation instance", Level.INFO, null); try { getEntityManager().persist(entity); EntityManager em=getEntityManager(); em.getTransaction().begin(); em.persist(entity); em.flush(); em.getTransaction().commit(); } catch (RuntimeException re) { EntityManagerHelper.log("save failed", Level.SEVERE, re); throw re; } } /** * Delete a persistent Goodsinformation entity. This operation must be * performed within the a database transaction context for the entity's data * to be permanently deleted from the persistence store, i.e., database. * This method uses the * {@link javax.persistence.EntityManager#remove(Object) * EntityManager#delete} operation. * * <pre> * EntityManagerHelper.beginTransaction(); * GoodsinformationDAO.delete(entity); * EntityManagerHelper.commit(); * entity = null; * </pre> * * @param entity * Goodsinformation entity to delete * @throws RuntimeException * when the operation fails */ public void delete(Goodsinformation entity) { EntityManagerHelper.log("deleting Goodsinformation instance", Level.INFO, null); try { EntityManager em=getEntityManager(); em.getTransaction().begin(); entity = em.getReference(Goodsinformation.class,entity.getGoodsId()); em.remove(entity); em.getTransaction().commit(); EntityManagerHelper.log("delete successful", Level.INFO, null); } catch (RuntimeException re) { EntityManagerHelper.log("delete failed", Level.SEVERE, re); throw re; } } /** * Persist a previously saved Goodsinformation entity and return it or a * copy of it to the sender. A copy of the Goodsinformation entity parameter * is returned when the JPA persistence mechanism has not previously been * tracking the updated entity. This operation must be performed within the * a database transaction context for the entity's data to be permanently * saved to the persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * entity = GoodsinformationDAO.update(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Goodsinformation entity to update * @return Goodsinformation the persisted Goodsinformation entity instance, * may not be the same * @throws RuntimeException * if the operation fails */ public Goodsinformation update(Goodsinformation entity) { EntityManagerHelper.log("updating Goodsinformation instance", Level.INFO, null); try { EntityManager em=getEntityManager(); em.getTransaction().begin(); Goodsinformation result = getEntityManager().merge(entity); em.getTransaction().commit(); EntityManagerHelper.log("update successful", Level.INFO, null); return result; } catch (RuntimeException re) { EntityManagerHelper.log("update failed", Level.SEVERE, re); throw re; } } public Goodsinformation findById(Integer id) { EntityManagerHelper.log("finding Goodsinformation instance with id: " + id, Level.INFO, null); try { EntityManager em=getEntityManager(); em.getTransaction().begin(); Goodsinformation instance =em.find(Goodsinformation.class, id); em.getTransaction().commit(); return instance; } catch (RuntimeException re) { EntityManagerHelper.log("find failed", Level.SEVERE, re); throw re; } } /** * Find all Goodsinformation entities with a specific property value. * * @param propertyName * the name of the Goodsinformation property to query * @param value * the property value to match * @return List<Goodsinformation> found by query */ @SuppressWarnings("unchecked") public List<Goodsinformation> findByProperty(String propertyName, final Object value) { EntityManagerHelper.log( "finding Goodsinformation instance with property: " + propertyName + ", value: " + value, Level.INFO, null); try { final String queryString = "select model from Goodsinformation model where model." + propertyName + "= :propertyValue"; Query query = getEntityManager().createQuery(queryString); query.setParameter("propertyValue", value); return query.getResultList(); } catch (RuntimeException re) { EntityManagerHelper.log("find by property name failed", Level.SEVERE, re); throw re; } } public List<Goodsinformation> findByGoodsName(Object goodsName) { return findByProperty(GOODS_NAME, goodsName); } public List<Goodsinformation> findByGoodsNum(Object goodsNum) { return findByProperty(GOODS_NUM, goodsNum); } public List<Goodsinformation> findByGoodsPrice(Object goodsPrice) { return findByProperty(GOODS_PRICE, goodsPrice); } /** * Find all Goodsinformation entities. * * @return List<Goodsinformation> all Goodsinformation entities */ @SuppressWarnings("unchecked") public List<Goodsinformation> findAll() { EntityManagerHelper.log("finding all Goodsinformation instances", Level.INFO, null); try { final String queryString = "select model from Goodsinformation model"; Query query = getEntityManager().createQuery(queryString); return query.getResultList(); } catch (RuntimeException re) { EntityManagerHelper.log("find all failed", Level.SEVERE, re); throw re; } } } 八、系统首页 8.1 核心程序 <div data-options="region:'west',title:'系统菜单',split:true" style="width:200px;"> <div id="aa" class="easyui-accordion" data-options="fit:true" style="width:300px;height:200px;"> <div title="系统管理" data-options="" style="overflow:auto;padding:10px;"> <ul> <li><a href="#" title="<%=request.getContextPath() %>/user/select.do">用户管理</a></li> <li><a href="#" title="<%=request.getContextPath() %>/product/select.do">商品管理</a></li> <li><a href="#" title="<%=request.getContextPath() %>/orders/select.do">订单管理</a></li> </ul> </div> <div title="库存管理" data-options="" style="padding:10px;"> <ul> <li><a href="#" title="<%=request.getContextPath() %>/inventory/in.do">入库管理</a></li> <li><a href="#" title="<%=request.getContextPath() %>/inventory/query.do">库存查询</a></li> </ul> </div> </div> </div>
最新发布
06-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值