I have two tables Employee and Address as shown:
public class Employee {
private Integer id;
private String firstName;
private String lastName;
private boolean employeeStatus;
private Address address;
//getters setters
}
public class Address {
private Integer id;
private String country;
private String city;
private String street;
private Integer emp_id;
//getters setters
}
#Repository("employeeDao")
public class EmployeeDaoImpl implements EmployeeDao {
private JdbcTemplate jdbcTemplate;
#Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
#Override
public void insertEmployee(Employee e)
{
String sql = "INSERT INTO tbl_employee (dept_id,firstName,lastName,employeeStatus) values(?,?,?,?,?)";
this.jdbcTemplate.update(sql,new
Object[]{e.getDept_id(),e.getFirstName(),e.getLastName(),e.isEmployeeStatus()});
// INSERT ADDRESS????
}
// Other Methods
}
Now i want to implement Transactional while inserting the employee and address table attributes. I am abit confused here. Does #transactional annotation over the method does the required job? So far i understood that. Also, is it best practice to insert address from where i am inserting employee attributes? I also read somewhere that the transactional should be implemented from service layer than Dao. How would transactional can be implemented in this case?
EDIT
Since it is recommended to use #transactional in service layer, service layer became like:
#Service("employeeService")
#Transactional
public class EmployeeServiceImpl implements EmployeeService{
#Autowired
EmployeeDao employeeDao;
#Autowired
AddressDao addressDao;
#Override
public void insertEmployee(Employee e) {
employeeDao.insertEmployee(e);
addressDao.insertAddress(e.address);
}
}
is it the right way to perform transactional? Also can anyone explain #Transactional(propagation = Propagation.SUPPORTS, readOnly = true) instead of plain #Transactional ?
Alltough the #Transactional annotation would do the job, transactions are usually defined on service level. This way one business call is in one transaction, making sure everything succeeds or fails together.
you can read about #transactional in combination with jdbctemplate here
Related
For example I have User entity class.
This User have a status field.
When status is changed I need to call method from my UserService.
I have this code, but I think injecting service into entity is very bad.
#Entity
public class User {
private String status;
#Transient
private String lastStatus;
#Transient
#Autowired
private UserService userService;
public String getStatus() {
return status;
}
public void setStatus(String status) {
lastStatus = this.status;
this.status = status;
}
#PreUpdate
public void handleChangeStatus() {
if (!status.equals(lastStatus)) {
userService.doSomething();
}
}
}
One way to do this is to publish domain events from your User entity class and then to create a listener for this events.
First, let's create an event:
#AllArgsConstructor
#Getter
public class UserStatusChangeEvent {
private final UUID userId;
private final String lastStatus;
private final String newStatus;
}
Then your User entity class should implement AbstractAggregateRoot interface with default domain event publishing mechanism to let your entity publish events and publish one in your setStatus method:
#Entity
#Getter
public class User implements AbstractAggregateRoot<User> {
private UUID userId;
private String status;
#Transient
private String lastStatus;
public void setStatus(String status) {
lastStatus = this.status;
this.status = status;
registerEvent(new UserStatusChangeEvent(userId, lastStatus, status));
}
}
Then create a separate class with a listener, define it as a bean (#Component) and inject there your UserService:
#RequiredArgsConstructor
#Component
public class UserStatusListener {
private final UserService userService;
#EventListener
public void onStatusChange(UserStatusChangeEvent statusChangeEvent) {
if (!statusChangeEvent.getNewStatus().equals(statusChangeEvent.getLastStatus())) {
userService.doSomething();
}
}
}
Spring will do the "magic" for you - publish your event as a application event and register your listener on startup.
Note, that spring-data will publish your domain events only after save or saveAll method called on a repository of your entity, so no save - no events.
Also instead of #EventListener you can use #TransactionalEventListener with different transaction phase if you want your listener to work depending on the transaction (success, failure, before or after, etc.).
P.S. I used Lombok annotations on classes to simplify code and inject fields through the constructor.
I am a beginner in programming and I am learning Java.
I'm building a CRUD application with spring MVC as an exercise, and I don't have a textbook for spring, I'm learning Spring by looking at textbooks for JavaEE.
The following is a quote from the textbook, but I would like to rewrite it into Spring.
How can I rewrite JavaEE to Spring?
I don't know how to write in Spring, especially around #EJB and #Inject.
The repository and service were written by me.
//Author entity
1 id
2 autherName
3 birthPlace
#Named
#ViewScoped
public class BookshelfService implements Serializable {
private Integer id;
private String autherName;
private String birthplace;
#EJB
Bookshelf bs;
#Inject
transient Logger log;
public String create() {
Author author = new Author (id , autherName,birthplace);
return null;
}
public String edit(Author author) {
id = author.getId();
autherName = author.autherName();
Birthplace = author.getBirthplace();
return null;
}
// getter setter...
}
#Stateless
public class Bookshelf {
#PersistenceContext
private EntityManager em;
public void create(Author author) {
em.persist(author);
}
}
//written by me
#Repository
public interface AutherRepository extends JpaRepository <AutherEntity, Long> {
}
//written by me
#Service
#Transactional
public class BookService {
#Autowired
private AutherRepository autherRepository;
private BookTitleRepository booktitleRepository;
}
I am new to spring.
I just tried successfully using an entity class without #Id in Spring Data JDBC
Custom query was added in my repository for retrieving data from 2 mysql tables and returning an entity having the joined table data.
If I plan to use only custom queries, am I missing anything here?
Here's my entity class without #Id or #Entity:
public class Item
{
private long id;
private String code;
private String itemName;
private String groupName;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
}
Repository layer:
#Repository
public interface ItemRepository extends PagingAndSortingRepository<Item, Long>
{
#Query("SELECT a.id, a.code, a.name AS item_name,
b.name as group_name from item a, item_group b
WHERE a.group_id = b.id AND a.id=:id")
Item findItemById(#Param("id") Long id);
}
Service layer:
#Service
public class ItemServiceImpl implements ItemService
{
private final ItemRepository itemRepository;
public ItemServiceImpl(ItemRepository itemRepository)
{
this.itemRepository = itemRepository;
}
#Override
#Transactional(readOnly=true)
public Item findItemById(Long id)
{
return itemRepository.findItemById(id);
}
}
My updated main Configuration class in response to answer of Jens:
#SpringBootApplication
#EnableJdbcRepositories
public class SpringDataJdbcApplication extends AbstractJdbcConfiguration
{
public static void main(String[] args)
{
SpringApplication.run(SpringDataJdbcApplication.class, args);
}
#Bean
#ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource()
{
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
return dataSourceBuilder.build();
}
#Bean
NamedParameterJdbcOperations namedParameterJdbcOperations(DataSource dataSource)
{
return new NamedParameterJdbcTemplate(dataSource);
}
#Bean
PlatformTransactionManager transactionManager()
{
return new DataSourceTransactionManager(dataSource());
}
}
If you don't get any exceptions you should be fine. There shouldn't be anything in Spring Data JDBC that silently breaks when the id is not specified.
The problem is though: I don't consider it a feature that this works, but just accidental behaviour. This means it might break with any version, although replacing these methods with custom implementations based on a NamedParameterJdbcTemplate shouldn't be to hard, so the risk is limited.
The question though is: Why don't you add the #Id annotation, after all your entity does have an id. And the whole idea of a repository conceptually requires an id.
If it's working and you really don't want to use the annotations, you can do it. But I think that it's unnecessary complication. You can expect errors that would not be there if you had used the annotations and code will be harder to debug. If you are new in Spring I recommend to use annotations. But after all it depend on you how will you design your applications. For sure advantage of approach without annotations is higher control about database.
I am trying to a write a spring boot controller which can return the result of a native query as json. I will be passing the query as input parameter and the return must be result of the query. Is there a way to do this? I know the http rpc help on this. The query can be anything and the system must accept it and must respond with the result as json.
For example if I pass the request as select * from employee it must respond with result of query as json.
Simply make every function returning:
Map<String, Object>
It will automatically map the object property and value. That means a json object is an instance of Map. If you are managing an array of it, enclose it with a List:
List<Map<String, Object>>
and finally the ResponseEntity becomes:
ResponseEntity<List<Map<String, Object>>>
You could actually use Spring JDBC for that,
Repo
#Repository
public class FooRepo {
#Autowire
private JdbcTemplate jdbcTemplate;
public Object returnDataForQuery(String sql) {
return jdbcTemplate.queryForObject(sql, Object.class); // You could define a proper class if you know the return Type else returning plain object is more then enough
// return jdbcTemplate.queryForList(sql, Object.class) Incase Multiple Data
}
}
Model
public class FooDto {
private String query;
// Getter, Setter & No Args Constructor (or) Lombok
}
Controller
#Autowire
private FooRepo fooRepo;
#PostMapping(value = "/postData", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity postData(#RequestBody FooDto foo) {
return ResponseEntity.ok(fooRepo.returnDataForQuery(foo.getQuery);
}
This is just a overview, you could bend it.As for your result output concern you ResponseEntity will take care of it
SpringBoot
//Controller Class
#RestController
#RequestMapping("/employee")
public class EmployeeController {
#Autowired
private EmployeeService employeeService;
#GetMapping("/all")
public List<Employee> getAllEmplpyee() {
logger.info("get All Employeee");
return employeeService.getAllEmployeeService();
}
}
//ServiceImpl
#Service
public class EmployeeService {
private static final Logger logger = LoggerFactory.getLogger(EmployeeService.class);
#Autowired
private EmployeeRepository employeeRepository;
public List<Employee> getAllEmployeeService() {
logger.info(getClass().getName()," invked getAllEmployee");
List<Employee> empBo = employeeRepository.findAll();
return copyPropertiesValues(empBo);
}
}
//DAO
#Component
public interface EmployeeRepository extends JpaRepository<Employee, String>{
}
//Model
#Entity
#Table(name = "employees")
public class Employee {
#Id
#Column(name = "employeeNumber",nullable=false)
private String employeeNumber;
#Column(nullable=false)
private String lastName;
#Column(nullable=false)
private String firstName;
#Column(nullable=false)
private String extension;
#Column(nullable=false)
private String email;
#Column( nullable=false)
private String officeCode;
#Column(nullable=false)
private String reportsTo;
#Column(nullable=false)
private String jobTitle;
//GETTER SETTER
}
//application.properties
spring.jpa.hibernate.ddl-auto=update
spring.jpa.open-in-view=true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.type.descriptor.sql=trace
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=****
I do everything according to http://docs.jboss.org/hibernate/orm/4.0/hem/en-US/html/listeners.html, but neither in-bean methods nor external ones are ever executed. What might be the cause?
#Entity
#EntityListeners(EntityListener.class)
public class User {
#Id
#Column
#GeneratedValue
private Integer id;
// etc...
#PostConstruct
#PostLoad
#PostPersist
#PostUpdate
public void magic() {
System.out.println("YES I AM EXECUTED!");
System.exit(123);
}
}
OR
#Entity
#EntityListeners(MyListener.class)
public class User {
#Id
#Column
#GeneratedValue
private Integer id;
// etc...
}
+
public class MyListener {
#PostPersist
void postPersist(Object object) {
System.out.println("CAN'T BELEIVE I SEE THIS!");
System.exit(234);
}
}
My code creates, saves and loads beans, but nothing happens on the listeners. This is a piece of the repository thats perform the operations:
#Repository
public class UserRepositoryImpl implements UserRepository {
#Autowired
private SessionFactory sessionFactory;
#Override
public User get(Integer id) {
return sessionFactory.getCurrentSession().get(User.class, id);
}
#Override
public User save(User user) {
Session session = sessionFactory.getCurrentSession();
user = (User) session.merge(user);
session.saveOrUpdate(user);
return user;
}
// etc...
}
Repository methods are called from services like this one:
#Service
#Transactional
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository userRepository;
#Override
public void something() {
// just some repo calls + extra logic
}
}
I do not think I got something special here.
JPA interceptors mechanism work only when you manipulate entities via JPA EntityManager, they have no effect when you're using Hibernate Session directly.
You'll have to implement Hibernate native interceptors if you want to use the Session API.