I am new to spring. I am developing a CRUD application using spring jdbc template. I am done with insert and select. but in update am facing some problem. can anybody provide me a simple example of update and delete using jdbctemplate.
thnks in advance.
MY CONTROLLER-
#RequestMapping(method = RequestMethod.GET)
public String showUserForm(#ModelAttribute(value="userview") User user,ModelMap model)
{
List list=userService.companylist();
model.addAttribute("list",list);
return "viewCompany";
}
#RequestMapping( method = RequestMethod.POST)
public String add(#ModelAttribute(value="userview") #Valid User user, BindingResult result)
{
userValidator.validate(user, result);
if (result.hasErrors()) {
return "viewCompany";
} else {
userService.updateCompany(user);
System.out.println("value updated");
return "updateSuccess";
}
when i click on update button the edited values should be updated in my DB according to the row ID , my problem is how to map the row id from jsp to controller.
Straight from the documentation:
The following example shows a column updated for a certain primary
key. In this example, an SQL statement has placeholders for row
parameters. The parameter values can be passed in as varargs or
alternatively as an array of objects. Thus primitives should be
wrapped in the primitive wrapper classes explicitly or using
auto-boxing.
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class ExecuteAnUpdate {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void setName(int id, String name) {
this.jdbcTemplate.update(
"update mytable set name = ? where id = ?",
name, id);
}
}
You can simply use request.getParamater() or command object to pass values from jsp to controller.
Related
I have Demand entity. I can update my entity without any problem but I think my approch have some security problem.
demandController
#RequestMapping(value = "/details/{id}", method = RequestMethod.POST)
public String updateDemand(#PathVariable("id") Long id, #Valid #ModelAttribute Demand demand, BindingResult result) {
if (result.hasErrors()) {
return "demandUpdateForm";
} else {
demand.setDemandId(id);
demandService.updateDemand(demand);
return "redirect:/demands";
}
}
serviceImpl
#Override
public Demand updateDemand(Demand demand) {
return demandRepository.save(demand);
}
form
<form id="vendorForm" th:action="#{/demands/details/__${demand.demandId}__}" th:object="${demand}" method="post" >
As you see I get DemandId from action. For example I want to update 5th id's demand and get the update form. Then I changed demandId via developer tools and click submit. If I modify id for example 2nd and form update my 2nd id demand not original the 5th one. How can I prevent this situation.
I think it would be better if you create unmanaged bean for this operations and will pass it as form backing bean.
public class DemandBean {
private Long id;
private String name;
...
// more fields
}
Controller :
#RequestMapping(value = "/details/update", method = RequestMethod.POST)
public String updateDemand(#Valid #ModelAttribute("demandBean") DemandBean demandBean, BindingResult result) {
if (result.hasErrors()) {
return "demandUpdateForm";
} else {
demandService.updateDemand(demandBean.getId(), demandBean.getName, ...);
return "redirect:/demands";
}
}
Service method :
#Override
public void updateDemand(Long id, String name, //etc) {
Demand d = id == null ? new Demand() : demandRepository.findOne(id);
d.setName(name);
// ...
// set other fields
return demandRepository.save(demand);
}
This approach helps you to avoid security leaks with passing id.
I'm using spring 3.2.5 via annotations and got some issue dealing with session.
My controller class is like this:
#Controller
public class WebController {
#Autowired
private IElementService elementService;
...
//in this method I set the "elementList" in session explicitly
#RequestMapping("/elementSearch.do")
public String elementSearch(
#RequestParam("keyword") String keyword,
HttpSession session){
List<Element> elementList= elementService.searchElement(keyword);
session.setAttribute("elementList", elementList);
return "searchResult";
}
//here I got my problem
#RequestMapping(value="/anotherMethod.do", produces="text/html; charset=utf-8")
#ResponseBody
public String anotherMethod(
...
//I called my service method here like
Element e = elementService.searchElement("something").get(0);
...
}
And I have a ElementServiceImpl class like this:
#Service
public class ElementServiceImpl implements IElementService {
#Autowired
private IBaseDAO baseDao;
#Override
public List<Metadata> searchElement(String keyword) {
List<Metadata> re = baseDao.searchElement(keyword);
return re;
}
}
And I have a BaseDAOImpl class implemented IBaseDAO and annonated with #Repository:
#Repository
public class BaseDAOImpl implements IBaseDAO {
...
}
Here is the problem, when I visit ".../anotherMethod.do", which will call the anotherMethod up there, my "elementList" in session was changed!
Then I looked into the anotherMethod() and found everytime
Element e = elementService.searchElement("something").get(0);
was called, my elementList was change to the new result returned by searchElement method(which returns a List).
But I didn't set session in that method, and I'm not using #SessionAttributes, so I don't understand how could my session attribute changed after calling a service method?
This problem is torturing me right now so any advise would be a great help, thanks!
update: I tried to print all my session attributes around that method call like this:
StringBuilder ss1 = new StringBuilder("-------------current session-------------\n");
session.setAttribute("test1", "test value 1");
log.info("sessionTest - key:test1 value:" + session.getAttribute("test"));
Enumeration<String> attrs1 = session.getAttributeNames();
while(attrs1.hasMoreElements()){
String key = attrs1.nextElement();
ss1.append(key).append(":").append(session.getAttribute(key)).append("\n");
}
log.info(ss1);
But I didn't see whether the "elementList" or the test value which I added just before print. And I do can get some value by
List<Element> elementList = (List<Element>) session.getAttribute("elementList");
and the elementList I get changed after calling service method, just like I posted before. Where my elementList stored? not in the session?
My goal is to show the elementList to the users in a table, and let them pick one of them, then I get the row number of the table and take it as a index of the elemntList, so I'll know which elemnt object the user picked. Are there any better way to do this so I can get rid of that problem?
Thanks again.
I'm new to JMock, trying to develop a Spring controller test. Here is my test method:
#Test
public void testList() {
context.checking(new Expectations() {{
Student student = new Student(767001);
oneOf(studentService).getByNumber(767001); will(returnValue(student));
}});
ModelMap model = new ModelMap();
Student student = new Student(767001);
model.addAttribute("student", student);
CourseRightController instance = new CourseRightController();
request.setMethod("GET");
Assert.assertEquals(studentService.getByNumber(767001),model.get(student));
The question is how I'm able to test if the model contains the right object and object values? ModelMap is not that flexible than e.g ModelAndWiew. I can't get access to model attributes so the last code line here is not how it should be.
I usually use the Model interface and then in a test super class I have code which allows me to get at things in the Model
#Ignore
public abstract class SpringControllerTestCase {
/**
* Spring Model object - initialised in #Before method.
*/
private Model model;
/**
* Initialise fields before each test case.
*/
#Before
public final void setUpAll() {
model = new ExtendedModelMap();
}
public final Model getModel() {
return model;
}
#SuppressWarnings("unchecked")
public <T> T getModelValue(final String key, final Class<T> clazz) {
return (T) getModel().asMap().get(key);
}
}
then in a test I can do
assertEquals("someValue", getModelValue("bean", String.class));
or
assertTrue(getModelValue("student", Student.class).getId() == "767001");
Note this is all just shorthand for code like this
Student student = (Student) model.asMap().get("student");
assertEquals(767001, student.getId());
You can use extended model map instead for more flexibility. And you should declare references using the interface not implementation.
There is also this package to be included in spring 3.2 which may help : https://github.com/SpringSource/spring-test-mvc
However I have always been fine using extendedmodelmap and plain old hashmaps.
In your example, have you implemented equals (and hashcode) correctly, if you have not overrridden these methods the assertEquals will be testing if the objects are the same reference.
I'm using #SessionAttributes on 2 controllers and am experiencing some very strange behavior. My first controller (ViewController) is simply a view controller that displays JSP pages. The other is a controller that handles Ajax requests (AjaxController). I have a session attribute that is simply an object that has a HashMap as a member. The object is a wrapper around the map. The map is populated from the database and put in the session, which displays fine via the ViewController. However, when I do a delete from the map via an ajax request (AjaxController) and refresh the page, ViewController SOMETIMES shows that the element is removed, yet other times the element is still there. Here's code snippets:
ViewController (the homepage simply displays the contents of the map contained by userSettings
#Controller
#SessionAttributes({"userSettings"})
public class ViewController {
#RequestMapping(value="/", method=RequestMethod.GET)
public String home(ModelMap model) {
UserSettings userSettings = (UserSettings) model.get("userSettings");
String userListenersJson = userSettings.toJson(); // for bootsrtapping the js on the front end
return "views/home";
}
}
AjaxController:
#Controller
#SessionAttributes({"userSettings"})
public class AjaxController {
#RequestMapping(value="/users/listeners/{externalId}", method=RequestMethod.DELETE)
public #ResponseBody
AjaxResponse<?> deleteListener(ModelMap model,
#PathVariable long externalId) {
UserSettings userSettings = (UserSettings) model.get("userSettings");
userSettings.removeSetting(externalId);
return new AjaxResponse<String>(null, true);
}
}
Am I using #SessionAttributes wrong here? Why would this work sometimes and not others? I've also tried putting all of the view and ajax functionality in the same controller and experienced the same behavior.
Thanks for any help!
EDIT:
I've refactored my code a bit to use the UserPrincipal via springsecurity. My understanding is that this object is stored in the session. Regardless, I'm seeing exactly the same behavior.
Here's the UserPrincipal constructor that populates the user settings map. I've set breakpoints here to ensure that the correct listenerDBOs are set - they are, every time. This is the only time the listeners get set from the db into the UserSettings object in CustomUserPrincipal. All other adds/removes are done via the controllers (quick aside: adds never fail... only removes):
public CustomUserPrincipal(UserDBO userDBO) {
// set UserSettings obj
UserSettingsAdapter.addListeners(userDBO.getUserListenerDBOs(), userSettings);
}
The UserSettings object itself:
public class UserSettings implements Serializable {
private static final long serialVersionUID = -1882864351438544088L;
private static final Logger log = Logger.getLogger(UserSettings.class);
private Map<Long, Listener> userListeners = Collections.synchronizedMap(new HashMap<Long, Listener>(1));
// get the listeners as an arraylist
public List<Listener> userListeners() {
return new ArrayList<Listener>(userListeners.values());
}
public Map<Long, Listener> getUserListeners() {
return userListeners;
}
public Listener addListener(Listener listener) {
userListeners.put(listener.getId(), listener);
return listener;
}
// I'm logging here to try and debug the issue. I do see the success
// message each time this function is called
public Listener removeListener(Long id) {
Listener l = userListeners.remove(id);
if (l == null) {
log.info("failed to remove listener with id " + id);
} else {
log.info("successfully removed listener with id " + id);
}
log.info("Resulting map: " + userListeners.toString());
log.info("Map hashcode: " + userListeners.hashCode());
return l;
}
public Listener getListener(long id) {
return userListeners.get(id);
}
}
This is the helper function in the UserSettingsAdapter class that adds to the UserSettings object, called from CustomUserDetails constructor:
public static void addListeners(Set<UserListenerDBO> userListeners, UserSettings userSettings) {
for (UserListenerDBO userListenerDBO : userListeners) {
if (userListenerDBO.isActive()) {
addListener(userListenerDBO, userSettings);
}
}
}
I've also changed the controller code to user the CustomUserPrincipal object instead of #SessionAttributes:
In ViewController:
#RequestMapping(value="/", method=RequestMethod.GET)
public String home(ModelMap model) {
CustomUserPrincipal userPrincipal = authenticationHelpers.getUserPrincipal();
UserSettings userSettings = userPrincipal.getUserSettings();
String userListenersJson = userSettings.toJson();
return "views/home";
}
In AjaxController:
#RequestMapping(value="/users/listeners/{externalId}", method=RequestMethod.DELETE)
public #ResponseBody
AjaxResponse<?> deleteListener(ModelMap model,
#PathVariable long externalId) {
CustomUserPrincipal userPrincipal = authenticationHelpers.getUserPrincipal();
UserSettings userSettings = userPrincipal.getUserSettings();
userSettings.removeListener(externalId);
return new AjaxResponse<String>(null, true);
}
I hope this helps shed some light on the issue!
I ran into a similar problem with #SessionAttributes. A controller had a #SessionAttributes annotation at the class level, and one of the methods handled POST requests, and included an instance of the session-managed object as an argument. This instance was saved to the database, but was re-used by subsequent requests, causing some data corruption. We had to add another method argument of type SessionStatus, and call SessionStatus.setComplete(). This caused the instance to be removed from the session, and prevented reuse and corruption. So try adding a SessionStatus instance to your controllers' handler methods, and invoke setComplete() where appropriate.
EDIT: I accidentally referenced the getter isComplete() in my initial answer; I meant to reference the setter setComplete().
#SessionAttributes is specific to a Controller and is not shared among several Controllers.
Instead, consider using manually session.setAttribute (class HttpSession).
You should have a look here : http://beholdtheapocalypse.blogspot.fr/2013/01/spring-mvc-framework-sessionattributes.html
I am using MyBatis and want to implement 2 fields on every table 'created', 'modified'. Both off them are date fields. Is there a way of automatically update these fields on an insert or update? Of course, I can adjust the mappings, but I was wondering if there is a more generic and DRY way of doing this?
No, mybatis has no mechanism to do this automatically without you coding your sql maps to update the columns.
One alternative would be database triggers. I'm not certain I would recommend that though, we just code it in the sql maps.
You could code it in the SQL maps like this,
<insert id="someInsert">
insert into dummy_table
(
SOME_COLUMN,
CREATED_DT
)
values
(
#{someValue},
sysdate
)
</insert>
or,
<update id="someUpdate">
update some_table
set some_column = #{someValue}, modified=sysdate
where some_id = #{someId}
</update>
you can use mybatis Interceptor
here is my example (using springboot):
in mycase, BaseEntity is the super class of all entity,i need do some thing before update or insert to database by mybatis.
step 1: create init method in BaseEntity for update or insert
public class BaseEntity{
private Date created;
private Date updated;
//getter,setter
public void initCreateEntity() {
this.created = new Date()
this.updated = new Date()
}
public void initUpdateEntity() {
this.created = new Date()
this.updated = new Date()
}
}
step 2: add a mybatis interceptor
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
/**
* add time interceptor for update
*/
#Intercepts(#Signature(type = Executor.class, method = "update", args={MappedStatement.class, Object.class}))
public class BaseEntityInterceptor implements Interceptor {
#Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
// get sql
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
// get parameter , this is the target object that you want to handle
Object parameter = invocation.getArgs()[1];
// make sure super class is BaseEntity
if (parameter instanceof BaseEntity) {
//init
BaseEntity baseEntity = (BaseEntity) parameter;
if (SqlCommandType.INSERT.equals(sqlCommandType)) {
baseEntity.initCreateEntity();
} else if (SqlCommandType.UPDATE.equals(sqlCommandType)) {
baseEntity.initUpdateEntity();
}
}
return invocation.proceed();
}
#Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
#Override
public void setProperties(Properties properties) {
}
}
step 3: add to bean Context in springboot config
#Configuration
public class MyBatisConfig {
#Bean
public BaseEntityInterceptor baseEntityInterceptor() {
return new BaseEntityInterceptor();
}
}
step 4: Dao and Mapper.xml
//base update or insert sql incloude column created and updated
eg:Dao
#Mapper
public interface BaseDao {
int update(BaseEntity baseEntity);
}
Mapper.xml
<update id="update" parameterType="com.package.to.BaseEntity">
update baseentity_table set created = #{createTime, jdbcType=TIMESTAMP}
updated = #{createTime, jdbcType=TIMESTAMP}
</update>
step 5: test
baseDao.update(new BaseEntity);
More information here: https://mybatis.org/mybatis-3/configuration.html#plugins