Есть ли в Java/Spring транзакции на изменение объектов?

Всем привет! В java как и в spring новичек.

Есть у меня объект, одновременно может юзаться во многих метсах (что-то типа приспособленца). Иногда его нужно изменить, но не одно свойство, а сразу несколько. Но это нужно обернуть в что-то наподобии транзакции, чтоб не получилось так, что клиент воспользуется им когда часть полей изменена, часть нет. Нужно чтоб либо пока идет изменнение, возвращались сатрые данние, либо чтоб чтоб поток ожидал (на подобии synchrnozed).

Есть ли для этого какие-то стандартные методы, или нужно подобное реализовывать самому?

Заранее спасибо!
  • Вопрос задан
  • 342 просмотра
Решения вопроса 1
EugeneP2
@EugeneP2
Java Dev
1-й вариант: Не использовать изменяемый объект, а каждый раз, когда вы хотите поменять что-то, создавать новый объект, при этом, сам объект должен быть immutable. Доступ к объекту осуществлять через фабрику. Такое решение имхо будит наиболее правильным.

Пример:

immutable класс

public final class User {

	private final String login;
	private final Date dateCreate;
	private final String description;
	private final Set<String> roles;

	public User(String login, Date dateCreate, String description, Set<String> roles) {
		this.login = login;
		this.dateCreate = (Date) dateCreate.clone();
		this.description = description;
		this.roles = Collections.unmodifiableSet(roles);
	}

	public String getLogin() {
		return login;
	}

	public Date getDateCreate() {
		return (Date) dateCreate.clone();
	}

	public String getDescription() {
		return description;
	}

	public Set<String> getRoles() {
		return roles;
	}
}


Фабрика

public class UserFactory<T> {

	private final AtomicReference<T> atomicReference;

	public UserFactory(T object) {
		this.atomicReference = new AtomicReference<>(object);
	}

	public T getObject() {
		return atomicReference.get();
	}

	public void setObject(T object) {
		atomicReference.set(object);
	}
}


public static void main(String[] args) {

		User user = new User("user1", new Date(), "test user", new HashSet<>(Arrays.asList("READ", "ADD")));

		UserFactory<User> userFactory = new UserFactory<>(user);

		exampleUsingUser(userFactory);

	}

	public static void exampleUsingUser(UserFactory<User> userFactory) {

		User user = userFactory.getObject();

		System.out.println(user.getLogin());
		System.out.println(user.getDescription());
		System.out.println(user.getRoles());
	}


2-й вариант: эдакий велосипед:) Контролировать доступ через прокси с использованием ReentrantReadWriteLock

Пример:

Для начала нам нужно выделить интерфейс класса, состояние объекта которого будет меняться

public interface Settings {

	String getColor();
	void setColor(String color);

	String getDescription();
	void setDescription(String description);

	int getSize();
	void setSize(int size);
}


Реализуем интерфейс

public class SettingsImpl implements Settings {

	private volatile String color;
	private volatile String description;
	private volatile int size;


	public static Settings newSettings() {
		return ProxySettingsKeeper.createProxy(new SettingsImpl());
	}
	
	private SettingsImpl() {
	}

	@Override
	public String getColor() {
		return color;
	}

	@Override
	public void setColor(String color) {
		this.color = color;
	}

	@Override
	public String getDescription() {
		return description;
	}

	@Override
	public void setDescription(String description) {
		this.description = description;
	}

	@Override
	public int getSize() {
		return size;
	}

	@Override
	public void setSize(int size) {
		this.size = size;
	}
}


Прокси

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ProxySettingsKeeper implements InvocationHandler {

	private final Settings settings;

	private final ReentrantReadWriteLock.ReadLock readLock;
	private final ReentrantReadWriteLock.WriteLock writeLock;

	public ProxySettingsKeeper(Settings settings) {

		ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
		this.readLock = readWriteLock.readLock();
		this.writeLock = readWriteLock.writeLock();

		this.settings = settings;
	}

	public static Settings createProxy(Settings settings) {
		return (Settings) Proxy.newProxyInstance(settings.getClass().getClassLoader()
				, new Class[]{Settings.class}
				, new ProxySettingsKeeper(settings));
	}


	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

		String name = method.getName();

		if (name.startsWith("get")) {

			readLock.lock();
			try {
				return method.invoke(settings, args);
			} finally {
				readLock.unlock();
			}

		} else if (name.startsWith("set")) {

			writeLock.lock();
			try {
				return method.invoke(settings, args);
			} finally {
				writeLock.unlock();
			}
		}

		return method.invoke(settings, args);
	}
}


public static void main(String[] args) {

		Settings settings = SettingsImpl.newSettings();

		settings.setDescription("test");

		System.out.println(settings.getDescription());
	}
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы