How can the proxy pattern be used to replace a singleton?
In my opinion, there is no "either, or". A class can implement several design patterns at the same time. I would say the class implementing the access to the external database is in any case a Proxy (a remote proxy in this case). If you consider the caching an extra functionality it is also a Decorator.
So, the real question is, should it also be a Singleton? Let us assume there is exactly one external database. Does there need to be only one CachingDBProxy? I would say, it depends on the use:
If there are several clients accessing similar data, they can clearly benefit if they share the same CachingDBProxy. The data needed by one client may already have been needed by another client, so it can be retrieved from the cache instead of having to perform a costly access to the database.
On the other hand, some clients may access quite distinct pieces of data. So, if we assume that the CachingDBProxy only caches a limited amount of data access by one group of clients may throw out data still needed by another group of clients, causing degradation of cache performance. In this case it may be reasonable to have multiple CachingDBProxies even though there is only one database (this assumes of course, that concurrent access is possible).
So, how many CachingDBProxies there should be, depends on the use. The CachingDBProxy should not limit its use without good reason, so it should not enforce that there is only one instance, So, in this case the CachingDBProxies should be no Singleton, IMHO. Only the clients can know how many CachingDBProxies are good for them.
On the other hand, if we have a Proxy for specific resource that can only handle one access at a time, it may have to be a Singleton. But that is a distinct case from the above case. Here the requirement comes directly from the area the Proxy is responsible for (its purpose is to channel the access to that specific resource).
disclaimer: I'm speaking in java terms here
singleton are now considered an antipattern mostly because have been abused a lot lately as they are a quick and convenient way to share data across the application - which is somewhat an overextension of the design pattern which is more suited to provide acces control to a shared resource.
consider a program standard output: the access of that resource needs to be guarded by a single point of access to allow for synchronization of the writes, that is why you have for example System.out as a static instance in java.
the problem is, when you start having singleton you'll need to know every nitty gritty details of what you're doing, because you are making lot of strict assumption on your singleton class, the most important one that it will be the only one class in the system. then you start using it, assuming that it will always be a single entry point to your resource, and then nasty bug arises because your class has now been deployed on an ejb server and each ejb context has it's own singleton, plus one more singleton for every jsp that has been reloaded from the server, plus one singleton for every time that your singleton has been serialized and deserialized (as you probably have forgot to override readResolve() method).
so this is why singleton have to be used with lot of care, and are now considered an antipattern in spite of them being totally useful for their intended usage.
in the case of a database cache, it would be a better approach to have each class in need of the cache using a proxy for this "cache" resource, so you may add the logic to "find the resource" within the proxy itself instead of having the logic be tied to the retrieval of the cache singleton, which may or may not work depending on the environment.
so in few words using singleton as means to have a shared access to a resource is bad, because you are hardcoding the method of finding the resource (and ignoring the singleton pitfalls) while having singleton to control a resource for synchronization purpose is totally acceptable.
think of semaphores, those works only if you can get the same semaphore always. in this latter case what may be a problem is accessing the singleton from everywhere you need to access that semaphore: here you'll need some class to wrap the singleton up and provide a finer control of the lifecycle of the semaphore itself.
proxy are intended to cover the role of "providing a resource across the system", be it a single application, a client server system, different components of the same system and so on, with the added benefit that with èrpxy the method of retrieval of the resource is opaque. you may have them providing you a singleton containing an hashmap of the cached values, you may have them accessing a memcached somwhere on the network, you may have them reading a csv during tests, all without changing how you call them from the application.